Enhancement : Color palettes

- Added clone feature when editing existing palette
- Added arrow up/down to increase decrease input values
- Paint.net palettes are supported
This commit is contained in:
jdescottes 2014-09-09 23:53:57 +02:00
parent 125e332b7c
commit 90845b3a62
9 changed files with 135 additions and 35 deletions

View File

@ -36,7 +36,7 @@
}
.tooltip {
position: absolute;
z-index: 1030;
z-index: 30000;
display: block;
visibility: visible;
padding: 5px;

View File

@ -112,7 +112,7 @@
position: absolute;
top: 0;
right: 0;
padding: 2px 4px;
padding: 2px 4px 0 0;
opacity : 0.2;
font-weight: bold;

View File

@ -1,10 +1,16 @@
(function () {
var ns = $.namespace('pskl.controller.dialogs');
var MODE = {
CREATE : 'CREATE',
EDIT : 'EDIT'
};
ns.CreatePaletteController = function (piskelController) {
this.paletteService = pskl.app.paletteService;
this.paletteImportService = pskl.app.paletteImportService;
this.selectedIndex = -1;
this.mode = null;
};
pskl.utils.inherit(ns.CreatePaletteController, ns.AbstractDialogController);
@ -18,6 +24,7 @@
this.nameInput = document.querySelector('input[name="palette-name"]');
var submitButton = document.querySelector('.create-palette-submit');
var cloneButton = document.querySelector('.create-palette-clone');
var cancelButton = document.querySelector('.create-palette-cancel');
var importFileButton = document.querySelector('.create-palette-import-button');
@ -26,6 +33,7 @@
this.hiddenFileInput.addEventListener('change', this.onFileInputChange_.bind(this));
submitButton.addEventListener('click', this.onSubmitButtonClick_.bind(this));
cloneButton.addEventListener('click', this.onCloneButtonClick_.bind(this));
cancelButton.addEventListener('click', this.closeDialog.bind(this));
importFileButton.addEventListener('click', this.onImportFileButtonClick_.bind(this));
@ -37,8 +45,12 @@
if (paletteId) {
var paletteObject = this.paletteService.getPaletteById(paletteId);
palette = pskl.model.Palette.fromObject(paletteObject);
importFileButton.style.display = 'none';
this.mode = MODE.EDIT;
} else {
palette = new pskl.model.Palette(pskl.utils.Uuid.generate(), 'New palette', []);
palette = new pskl.model.Palette(pskl.utils.Uuid.generate(), 'New palette', ['#000000']);
cloneButton.style.display = 'none';
this.mode = MODE.CREATE;
}
this.setPalette_(palette);
@ -47,6 +59,7 @@
ns.CreatePaletteController.prototype.setPalette_ = function (palette) {
this.palette = palette;
this.nameInput.value = pskl.utils.unescapeHtml(this.palette.name);
this.selectColor_(0);
this.refresh_();
};
@ -111,6 +124,12 @@
this.closeDialog();
};
ns.CreatePaletteController.prototype.onCloneButtonClick_ = function (evt) {
var palette = new pskl.model.Palette(pskl.utils.Uuid.generate(), this.palette.name, this.palette.colors);
this.paletteService.savePalette(palette);
this.closeDialog();
};
ns.CreatePaletteController.prototype.onImportFileButtonClick_ = function () {
this.hiddenFileInput.click();
};

View File

@ -11,6 +11,7 @@
var isChromeOrFirefox = pskl.utils.UserAgent.isChrome || pskl.utils.UserAgent.isFirefox;
var changeEvent = isChromeOrFirefox ? 'input' : 'change';
this.container.addEventListener(changeEvent, this.onPickerChange_.bind(this));
this.container.addEventListener('keydown', this.onKeydown_.bind(this));
this.spectrumEl = this.container.querySelector('.color-picker-spectrum');
@ -25,7 +26,6 @@
this.setColor("#000000");
};
ns.HslRgbColorPicker.prototype.onPickerChange_ = function (evt) {
var target = evt.target;
@ -53,6 +53,27 @@
this.setColor(color);
};
ns.HslRgbColorPicker.prototype.onKeydown_ = function (evt) {
var target = evt.target;
if (target.getAttribute('type').toLowerCase() === 'text') {
var value = parseInt(target.value, 10);
var dimension = target.dataset.dimension;
var key = pskl.service.keyboard.KeycodeTranslator.toChar(evt.keyCode);
if (key === 'up') {
value = value + 1;
} else if (key === 'down') {
value = value - 1;
}
value = this.normalizeDimension_(value, dimension);
target.value = value;
this.onPickerChange_(evt);
}
};
ns.HslRgbColorPicker.prototype.setColor = function (inputColor) {
if (!this.unplugged) {
this.unplugged = true;
@ -150,5 +171,18 @@
}
};
ns.HslRgbColorPicker.prototype.normalizeDimension_ = function (value, dimension) {
var ranges = {
'h' : [0, 359],
's' : [0, 100],
'v' : [0, 100],
'r' : [0, 255],
'g' : [0, 255],
'b' : [0, 255]
};
var range = ranges[dimension];
return Math.max(range[0], Math.min(range[1], value));
} ;
})();

View File

@ -504,8 +504,10 @@
$(doc).bind("mousedown.spectrum", onMousedown);
// Piskel-specific : change the color as soon as the user does a mouseup
$(doc).bind("mouseup.spectrum", updateColor);
if (!flat) {
// Piskel-specific : change the color as soon as the user does a mouseup
$(doc).bind("mouseup.spectrum", updateColor);
}
$(window).bind("resize.spectrum", resize);
replacer.addClass("sp-active");

View File

@ -1,18 +1,15 @@
(function () {
var ns = $.namespace('pskl.service.palette');
var supportedFileFormats = ['gpl'];
var fileReaders = {
'gpl' : ns.PaletteGplReader,
'txt' : ns.PaletteTxtReader
};
ns.PaletteImportService = function () {};
ns.PaletteImportService.prototype.read = function (file, onSuccess, onError) {
var reader;
if (this.isImage_(file)){
reader = new ns.PaletteImageReader(file, onSuccess, onError);
} else if (this.isSupportedFormat_(file)) {
reader = this.getFileReader_(file, onSuccess, onError);
}
var reader = this.getFileReader_(file, onSuccess, onError);
if (reader) {
reader.read();
} else {
@ -25,19 +22,24 @@
};
ns.PaletteImportService.prototype.getFileReader_ = function (file, onSuccess, onError) {
var extension = this.getExtension_(file);
if (extension === 'gpl') {
return new ns.PaletteGplReader(file, onSuccess, onError);
var readerClass = this.getReaderClass_(file);
var reader = null;
if (readerClass) {
reader = new readerClass(file, onSuccess, onError);
}
return reader;
};
ns.PaletteImportService.prototype.isSupportedFormat_ = function (file) {
ns.PaletteImportService.prototype.getReaderClass_ = function (file) {
var extension = this.getExtension_(file);
return supportedFileFormats.indexOf(extension) != -1;
return fileReaders[extension];
};
ns.PaletteImportService.prototype.getExtension_ = function (file) {
var parts = file.name.split('.');
return parts[parts.length-1];
var extension = parts[parts.length-1];
return extension.toLowerCase();
};
})();

View File

@ -0,0 +1,38 @@
(function () {
var ns = $.namespace('pskl.service.palette');
var RE_COLOR_LINE = /^[A-F0-9]{2}([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})/;
ns.PaletteTxtReader = function (file, onSuccess, onError) {
this.file = file;
this.onSuccess = onSuccess;
this.onError = onError;
};
ns.PaletteTxtReader.prototype.read = function () {
pskl.utils.FileUtils.readFile(this.file, this.onFileLoaded_.bind(this));
};
ns.PaletteTxtReader.prototype.onFileLoaded_ = function (content) {
var text = pskl.utils.Base64.toText(content);
var lines = text.match(/[^\r\n]+/g);
var colorLines = lines.filter(function (l) {
return RE_COLOR_LINE.test(l);
});
var colors = colorLines.map(function (l) {
var matches = l.match(RE_COLOR_LINE);
var color = "#" + matches[1] + matches[2] + matches[3];
return color;
});
if (colors.length) {
var uuid = pskl.utils.Uuid.generate();
var palette = new pskl.model.Palette(uuid, 'Imported palette', colors);
this.onSuccess(palette);
} else {
this.onError();
}
};
})();

View File

@ -120,9 +120,10 @@
"js/service/BeforeUnloadService.js",
"js/service/HistoryService.js",
"js/service/palette/PaletteService.js",
"js/service/palette/PaletteImportService.js",
"js/service/palette/PaletteTxtReader.js",
"js/service/palette/PaletteGplReader.js",
"js/service/palette/PaletteImageReader.js",
"js/service/palette/PaletteImportService.js",
"js/service/SavedStatusService.js",
"js/service/keyboard/ShortcutService.js",
"js/service/keyboard/KeycodeTranslator.js",

View File

@ -8,7 +8,10 @@
<span class="create-palette-name-label">Name</span>
<input type="text" class="textfield create-palette-name-input" name="palette-name" placeholder="palette name ..."/>
<div class="create-palette-import-section">
<button type="button" class="button button-primary create-palette-import-button">Import from file</button>
<button
type="button"
rel="tooltip" data-placement="right" title="Import palette from an existing Image or from a palette file"
class="button button-primary create-palette-import-button">Import from file</button>
<input style="display:none"
class="create-palette-import-input"
type="file" value="file" accept="*"/>
@ -20,40 +23,41 @@
<div class="color-picker-spectrum"></div>
<div class="color-picker-slider">
<span>H</span>
<input type="range" data-model="hsv" data-dimension="h" value="0" min="0" max="359"/>
<input type="text" data-model="hsv" data-dimension="h" class="textfield" value="0"/>
<input type="range" data-model="hsv" data-dimension="h" value="0" min="0" max="359" tabindex="-1"/>
<input type="text" data-model="hsv" data-dimension="h" class="textfield" value="0" tabindex="101"/>
</div>
<div class="color-picker-slider">
<span>S</span>
<input type="range" data-model="hsv" data-dimension="s" value="0" min="0" max="100"/>
<input type="text" data-model="hsv" data-dimension="s" class="textfield" value="0"/>
<input type="range" data-model="hsv" data-dimension="s" value="0" min="0" max="100" tabindex="-1"/>
<input type="text" data-model="hsv" data-dimension="s" class="textfield" value="0" tabindex="102"/>
</div>
<div class="color-picker-slider">
<span>V</span>
<input type="range" data-model="hsv" data-dimension="v" value="0" min="0" max="100"/>
<input type="text" data-model="hsv" data-dimension="v" class="textfield" value="0"/>
<input type="range" data-model="hsv" data-dimension="v" value="0" min="0" max="100" tabindex="-1"/>
<input type="text" data-model="hsv" data-dimension="v" class="textfield" value="0" tabindex="103"/>
</div>
<br/>
<div class="color-picker-slider">
<span>R</span>
<input type="range" data-model="rgb" data-dimension="r" value="0" min="0" max="255"/>
<input type="text" data-model="rgb" data-dimension="r" class="textfield" value="0"/>
<input type="range" data-model="rgb" data-dimension="r" value="0" min="0" max="255" tabindex="-1"/>
<input type="text" data-model="rgb" data-dimension="r" class="textfield" value="0" tabindex="104"/>
</div>
<div class="color-picker-slider">
<span>G</span>
<input type="range" data-model="rgb" data-dimension="g" value="0" min="0" max="255"/>
<input type="text" data-model="rgb" data-dimension="g" class="textfield" value="0"/>
<input type="range" data-model="rgb" data-dimension="g" value="0" min="0" max="255" tabindex="-1"/>
<input type="text" data-model="rgb" data-dimension="g" class="textfield" value="0" tabindex="105"/>
</div>
<div class="color-picker-slider">
<span>B</span>
<input type="range" data-model="rgb" data-dimension="b" value="0" min="0" max="255"/>
<input type="text" data-model="rgb" data-dimension="b" class="textfield" value="0"/>
<input type="range" data-model="rgb" data-dimension="b" value="0" min="0" max="255" tabindex="-1"/>
<input type="text" data-model="rgb" data-dimension="b" class="textfield" value="0" tabindex="106"/>
</div>
<div class="color-preview"></div>
</div>
</div>
<div class="create-palette-actions">
<button type="button" name="create-palette-cancel" class="button button-primary create-palette-cancel">Cancel</button>
<button type="button" name="create-palette-cancel" class="button create-palette-cancel">Cancel</button>
<button type="button" name="create-palette-clone" class="button button-primary create-palette-clone">Save as new</button>
<button type="button" name="create-palette-submit" class="button button-primary create-palette-submit">Save</button>
</div>
</div>