add color picker

It wraps the upstream color picker, that is designed for DBus access.
There is a shortcut and a menu button.
This commit is contained in:
abakkk 2020-09-19 15:43:30 +02:00
parent 9977a037ec
commit af694382c8
8 changed files with 105 additions and 9 deletions

75
area.js
View File

@ -29,6 +29,7 @@ const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const System = imports.system;
@ -62,7 +63,7 @@ var Tools = Object.assign({
}, Shapes, Manipulations);
Object.defineProperty(Tools, 'getNameOf', { enumerable: false });
const getClutterColorFromString = function(string, fallback) {
const getColorFromString = function(string, fallback) {
let [success, color] = Clutter.Color.from_string(string);
color.toString = () => string;
if (success)
@ -70,10 +71,14 @@ const getClutterColorFromString = function(string, fallback) {
log(`${Me.metadata.uuid}: "${string}" color cannot be parsed.`);
color = Clutter.Color.get_static(Clutter.StaticColor[fallback.toUpperCase()]);
color.toString = () => fallback.slice(0, 1).toUpperCase() + fallback.slice(1);
color.toString = () => fallback;
return color;
};
const getColorFromRGB = function(red, green, blue) {
return getColorFromString(`rgb(${red},${green},${blue})`, 'White');
};
// DrawingArea is the widget in which we draw, thanks to Cairo.
// It creates and manages a DrawingElement for each "brushstroke".
// It handles pointer/mouse/(touch?) events and some keyboard events.
@ -152,7 +157,7 @@ var DrawingArea = new Lang.Class({
set currentPalette(palette) {
this._currentPalette = palette;
this.colors = palette[1].map(colorString => getClutterColorFromString(colorString, 'white'));
this.colors = palette[1].map(colorString => getColorFromString(colorString, 'White'));
if (!this.colors[0])
this.colors.push(Clutter.Color.get_static(Clutter.StaticColor.WHITE));
},
@ -254,9 +259,9 @@ var DrawingArea = new Lang.Class({
this.squareAreaSize = Me.drawingSettings.get_uint('square-area-size');
}
this.areaBackgroundColor = getClutterColorFromString(Me.drawingSettings.get_string('background-color'), 'black');
this.areaBackgroundColor = getColorFromString(Me.drawingSettings.get_string('background-color'), 'Black');
this.gridColor = getClutterColorFromString(Me.drawingSettings.get_string('grid-color'), 'gray');
this.gridColor = getColorFromString(Me.drawingSettings.get_string('grid-color'), 'Gray');
if (Me.drawingSettings.get_boolean('grid-line-auto')) {
this.gridLineSpacing = Math.round(this.monitor.width / (5 * GRID_TILES_HORIZONTAL_NUMBER));
this.gridLineWidth = this.gridLineSpacing / 20;
@ -1032,6 +1037,62 @@ var DrawingArea = new Lang.Class({
});
},
_onColorPicked: function(color) {
let { red, green, blue } = color;
this.currentColor = getColorFromRGB(red, green, blue);
if (this.currentElement) {
this.currentElement.color = this.currentColor;
this._redisplay();
}
this.emit('show-osd', Files.Icons.COLOR, String(this.currentColor), this.currentColor.to_string().slice(0, 7), -1, false);
this.initPointerCursor();
},
pickColor: function() {
if (!Screenshot.PickPixel)
// GS 3.28-
return;
// Translators: It is displayed in an OSD notification to ask the user to start picking, so it should use the imperative mood.
this.emit('show-osd', Files.Icons.COLOR_PICKER, pgettext("osd-notification", "Pick a color"), "", -1, false);
try {
let screenshot = new Shell.Screenshot();
let pickPixel = new Screenshot.PickPixel(screenshot);
if (pickPixel.pickAsync) {
pickPixel.pickAsync().then(result => {
if (result instanceof Clutter.Color) {
// GS 3.38+
this._onColorPicked(result);
} else {
// GS 3.36
let graphenePoint = result;
screenshot.pick_color(graphenePoint.x, graphenePoint.y, (o, res) => {
let [, color] = screenshot.pick_color_finish(res);
this._onColorPicked(color);
});
}
}).catch(() => this.initPointerCursor());
} else {
// GS 3.34-
pickPixel.show();
pickPixel.connect('finished', (pickPixel, coords) => {
if (coords)
screenshot.pick_color(...coords, (o, res) => {
let [, color] = screenshot.pick_color_finish(res);
this._onColorPicked(color);
});
else
this.initPointerCursor();
});
}
} catch(e) {
log(`${Me.metadata.uuid}: color picker failed: ${e.message}`);
this.initPointerCursor();
}
},
toggleHelp: function() {
if (this.helper.visible) {
this.helper.hideHelp();
@ -1120,7 +1181,7 @@ var DrawingArea = new Lang.Class({
elements.push(...JSON.parse(json.contents).map(object => {
if (object.color)
object.color = getClutterColorFromString(object.color, 'white');
object.color = getColorFromString(object.color, 'White');
if (object.font && typeof object.font == 'string')
object.font = Pango.FontDescription.from_string(object.font);
if (object.image)
@ -1227,7 +1288,7 @@ var DrawingArea = new Lang.Class({
this.elements.push(...JSON.parse(json.contents).map(object => {
if (object.color)
object.color = getClutterColorFromString(object.color, 'white');
object.color = getColorFromString(object.color, 'White');
if (object.font && typeof object.font == 'string')
object.font = Pango.FontDescription.from_string(object.font);
if (object.image)

View File

@ -225,6 +225,7 @@ const AreaManager = new Lang.Class({
'save-as-json': this.activeArea.saveAsJson.bind(this.activeArea, true, null),
'open-previous-json': this.activeArea.loadPreviousJson.bind(this.activeArea),
'open-next-json': this.activeArea.loadNextJson.bind(this.activeArea),
'pick-color': this.activeArea.pickColor.bind(this.activeArea),
'toggle-background': this.activeArea.toggleBackground.bind(this.activeArea),
'toggle-grid': this.activeArea.toggleGrid.bind(this.activeArea),
'toggle-square-area': this.activeArea.toggleSquareArea.bind(this.activeArea),

View File

@ -41,6 +41,7 @@ const ICON_NAMES = [
'tool-ellipse', 'tool-line', 'tool-mirror', 'tool-move', 'tool-none', 'tool-polygon', 'tool-polyline', 'tool-rectangle', 'tool-resize',
];
const ThemedIconNames = {
COLOR_PICKER: 'color-select-symbolic',
ENTER: 'applications-graphics', LEAVE: 'application-exit',
GRAB: 'input-touchpad', UNGRAB: 'touchpad-disabled',
OPEN: 'document-open', SAVE: 'document-save',

View File

@ -10,7 +10,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Draw On Your Screen\n"
"Report-Msgid-Bugs-To: https://framagit.org/abakkk/DrawOnYourScreen/issues\n"
"POT-Creation-Date: 2020-09-18 11:37+0200\n"
"POT-Creation-Date: 2020-09-19 15:32+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -47,6 +47,11 @@ msgstr ""
msgid "Type your text and press <i>%s</i>"
msgstr ""
#. Translators: It is displayed in an OSD notification to ask the user to start picking, so it should use the imperative mood.
msgctxt "osd-notification"
msgid "Pick a color"
msgstr ""
#. Translators: "released" as the opposite of "grabbed"
msgid "Keyboard and pointer released"
msgstr ""
@ -284,6 +289,10 @@ msgstr ""
msgid "Color"
msgstr ""
#. Translators: It is displayed in a menu button tooltip or as a shortcut action description, so it should NOT use the imperative mood.
msgid "Pick a color"
msgstr ""
msgid "Add to images"
msgstr ""

11
menu.js
View File

@ -483,6 +483,17 @@ var DrawingMenu = new Lang.Class({
item.icon.set_gicon(icon);
item.icon.set_style(`color:${this.area.currentColor.to_string().slice(0, 7)};`);
if (GS_VERSION >= '3.30') {
let colorPickerCallback = () => {
this.close();
this.area.pickColor();
};
// Translators: It is displayed in a menu button tooltip or as a shortcut action description, so it should NOT use the imperative mood.
let colorPickerButton = new ActionButton(_("Pick a color"), Files.Icons.COLOR_PICKER, colorPickerCallback, null, true);
let index = getActor(item).get_children().length - 1;
getActor(item).insert_child_at_index(colorPickerButton, index);
}
item.menu.itemActivated = item.menu.close;
this._populateColorSubMenu();

Binary file not shown.

View File

@ -164,6 +164,10 @@
<default>["&lt;Primary&gt;v"]</default>
<summary>Add images from the clipboard</summary>
</key>
<key type="as" name="pick-color">
<default><![CDATA[['<Primary>KP_0','<Primary>0']]]></default>
<summary>Pick a color</summary>
</key>
<key type="as" name="redo">
<default>["&lt;Primary&gt;&lt;Shift&gt;z"]</default>
<summary>Redo last brushstroke</summary>

View File

@ -46,7 +46,7 @@ var INTERNAL_KEYBINDINGS = [
['undo', 'redo', 'delete-last-element', 'smooth-last-element'],
['select-none-shape', 'select-line-shape', 'select-ellipse-shape', 'select-rectangle-shape', 'select-polygon-shape', 'select-polyline-shape',
'select-text-shape', 'select-image-shape', 'select-move-tool', 'select-resize-tool', 'select-mirror-tool'],
['switch-fill', 'switch-fill-rule', 'switch-color-palette', 'switch-color-palette-reverse'],
['switch-fill', 'switch-fill-rule', 'switch-color-palette', 'switch-color-palette-reverse', 'pick-color'],
['increment-line-width', 'increment-line-width-more', 'decrement-line-width', 'decrement-line-width-more',
'switch-linejoin', 'switch-linecap', 'switch-dash'],
['switch-font-family', 'switch-font-family-reverse', 'switch-font-weight', 'switch-font-style', 'switch-text-alignment'],
@ -55,6 +55,15 @@ var INTERNAL_KEYBINDINGS = [
['open-next-json', 'open-previous-json', 'save-as-json', 'export-to-svg', 'open-preferences', 'toggle-help'],
];
if (GS_VERSION < '3.30') {
// Remove 'pick-color' keybinding.
INTERNAL_KEYBINDINGS.forEach(settingKeys => {
let index = settingKeys.indexOf('pick-color');
if (index != -1)
settingKeys.splice(index, 1);
});
}
if (GS_VERSION < '3.36') {
// Remove 'open-preferences' keybinding.
INTERNAL_KEYBINDINGS.forEach(settingKeys => {