diff --git a/area.js b/area.js index dadec04..8945954 100644 --- a/area.js +++ b/area.js @@ -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) diff --git a/extension.js b/extension.js index 1363f17..6a232fc 100644 --- a/extension.js +++ b/extension.js @@ -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), diff --git a/files.js b/files.js index c91b404..dfefc5d 100644 --- a/files.js +++ b/files.js @@ -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', diff --git a/locale/draw-on-your-screen.pot b/locale/draw-on-your-screen.pot index 3b2aa41..3ad0099 100644 --- a/locale/draw-on-your-screen.pot +++ b/locale/draw-on-your-screen.pot @@ -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 \n" "Language-Team: LANGUAGE \n" @@ -47,6 +47,11 @@ msgstr "" msgid "Type your text and press %s" 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 "" diff --git a/menu.js b/menu.js index 53364bf..7d00347 100644 --- a/menu.js +++ b/menu.js @@ -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(); diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled index 76b17ef..ec56cc3 100644 Binary files a/schemas/gschemas.compiled and b/schemas/gschemas.compiled differ diff --git a/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml b/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml index d7a9158..b7abae0 100644 --- a/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml +++ b/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml @@ -164,6 +164,10 @@ ["<Primary>v"] Add images from the clipboard + + KP_0','0']]]> + Pick a color + ["<Primary><Shift>z"] Redo last brushstroke diff --git a/shortcuts.js b/shortcuts.js index df753d4..2b2d1ac 100644 --- a/shortcuts.js +++ b/shortcuts.js @@ -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 => {