From 1172e9da6fa70c9d8aaa4f9a3614c0cf4ea31769 Mon Sep 17 00:00:00 2001 From: abakkk Date: Thu, 30 Jul 2020 11:13:23 +0200 Subject: [PATCH] new image shape Images files are picked from `~/DrawOnYourScreen/images/` (and `extensiondir/images/`) svg, png, jpeg, ...(pixbuf) --- README.md | 6 +- area.js | 56 +++++++- data/images/Example.svg | 43 ++++++ elements.js | 96 ++++++++++++- extension.js | 4 + files.js | 132 ++++++++++++++++++ locale/draw-on-your-screen.pot | 9 ++ menu.js | 42 +++--- prefs.js | 2 + schemas/gschemas.compiled | Bin 4280 -> 4428 bytes ...extensions.draw-on-your-screen.gschema.xml | 12 +- 11 files changed, 372 insertions(+), 30 deletions(-) create mode 100644 data/images/Example.svg create mode 100644 files.js diff --git a/README.md b/README.md index f086e4c..83a00b0 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Then save your beautiful work by taking a screenshot. ## Features -* Basic shapes (rectangle, circle, ellipse, line, curve, text, free) +* Basic shapes (rectangle, circle, ellipse, line, curve, text, image, free) * Basic transformations (move, rotate, resize, stretch, mirror, inverse) * Smooth stroke * Draw over applications @@ -40,6 +40,10 @@ Then save your beautiful work by taking a screenshot. ![How to duplicate an element](https://framagit.org/abakkk/DrawOnYourScreen/-/raw/ressources/duplicate.webm) +* Insertable images: + + Add your images (jpeg, png, svg) to `~/.local/share/drawOnYourScreen/images/`. + * Screenshot Tool extension: [Screenshot Tool](https://extensions.gnome.org/extension/1112/screenshot-tool/) is a convenient extension to “create, copy, store and upload screenshots”. In order to select a screenshoot area with your pointer while keeping the drawing in place, you need first to tell DrawOnYourScreen to ungrab the pointer (`Ctrl + Super + Alt + D`). diff --git a/area.js b/area.js index a6a2172..0a65586 100644 --- a/area.js +++ b/area.js @@ -30,6 +30,7 @@ const Gtk = imports.gi.Gtk; const Lang = imports.lang; const Pango = imports.gi.Pango; const St = imports.gi.St; +const System = imports.system; const ExtensionUtils = imports.misc.extensionUtils; const Main = imports.ui.main; @@ -39,6 +40,7 @@ const Me = ExtensionUtils.getCurrentExtension(); const Convenience = ExtensionUtils.getSettings ? ExtensionUtils : Me.imports.convenience; const Extension = Me.imports.extension; const Elements = Me.imports.elements; +const Files = Me.imports.files; const Menu = Me.imports.menu; const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext; @@ -46,10 +48,13 @@ const CAIRO_DEBUG_EXTENDS = false; const SVG_DEBUG_EXTENDS = false; const TEXT_CURSOR_TIME = 600; // ms -const { Shapes, Manipulations, Transformations, LineCapNames, LineJoinNames, FillRuleNames, +const { Shapes, ShapeNames, Transformations, LineCapNames, LineJoinNames, FillRuleNames, FontWeightNames, FontStyleNames, FontStretchNames, FontVariantNames } = Elements; +const Manipulations = { MOVE: 100, RESIZE: 101, MIRROR: 102 }; +const ManipulationNames = { 100: "Move", 101: "Resize", 102: "Mirror" }; var Tools = Object.assign({}, Shapes, Manipulations); -var ToolNames = { 0: "Free drawing", 1: "Line", 2: "Ellipse", 3: "Rectangle", 4: "Text", 5: "Polygon", 6: "Polyline", 100: "Move", 101: "Resize", 102: "Mirror" }; +var ToolNames = Object.assign({}, ShapeNames, ManipulationNames); + var FontGenericNames = { 0: 'Theme', 1: 'Sans-Serif', 2: 'Serif', 3: 'Monospace', 4: 'Cursive', 5: 'Fantasy' }; var getDateString = function() { @@ -96,6 +101,7 @@ var DrawingArea = new Lang.Class({ Name: 'DrawOnYourScreenDrawingArea', Extends: St.DrawingArea, Signals: { 'show-osd': { param_types: [GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_DOUBLE, GObject.TYPE_BOOLEAN] }, + 'show-osd-gicon': { param_types: [Gio.Icon.$gtype, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_DOUBLE, GObject.TYPE_BOOLEAN] }, 'update-action-mode': {}, 'leave-drawing-mode': {} }, @@ -113,6 +119,7 @@ var DrawingArea = new Lang.Class({ this.undoneElements = []; this.currentElement = null; this.currentTool = Shapes.NONE; + this.currentImage = 0; this.currentFontGeneric = 0; this.isSquareArea = false; this.hasGrid = false; @@ -169,6 +176,13 @@ var DrawingArea = new Lang.Class({ this.currentFillRule = evenodd ? Cairo.FillRule.EVEN_ODD : Cairo.FillRule.WINDING; }, + getImages() { + let images = Files.getImages(); + if (!images[this.currentImage]) + this.currentImage = Math.max(images.length - 1, 0); + return images; + }, + vfunc_repaint: function() { let cr = this.get_context(); @@ -179,6 +193,8 @@ var DrawingArea = new Lang.Class({ } cr.$dispose(); + if (this.elements.some(element => element.shape == Shapes.IMAGE) || this.currentElement && this.currentElement.shape == Shapes.IMAGE) + System.gc(); }, _redisplay: function() { @@ -266,7 +282,7 @@ var DrawingArea = new Lang.Class({ if (this.currentElement) { cr.save(); this.currentElement.buildCairo(cr, { showTextCursor: this.textHasCursor, - showTextRectangle: this.currentElement.shape == Shapes.TEXT && !this.isWriting, + showTextRectangle: this.currentElement.shape != Shapes.TEXT || !this.isWriting, dummyStroke: this.currentElement.fill && this.currentElement.line.lineWidth == 0 }); cr.stroke(); @@ -477,6 +493,8 @@ var DrawingArea = new Lang.Class({ if (duplicate) { // deep cloning let copy = new this.grabbedElement.constructor(JSON.parse(JSON.stringify(this.grabbedElement))); + if (this.grabbedElement.image) + copy.image = this.grabbedElement.image; this.elements.push(copy); this.grabbedElement = copy; } @@ -574,6 +592,18 @@ var DrawingArea = new Lang.Class({ textRightAligned: this.currentTextRightAligned, points: [] }); + } else if (this.currentTool == Shapes.IMAGE) { + let images = this.getImages(); + if (!images.length) + return; + this.currentElement = new Elements.DrawingElement({ + shape: this.currentTool, + color: this.currentColor.to_string(), + eraser: eraser, + image: images[this.currentImage], + operator: this.currentOperator, + points: [] + }); } else { this.currentElement = new Elements.DrawingElement({ shape: this.currentTool, @@ -939,6 +969,15 @@ var DrawingArea = new Lang.Class({ this.emit('show-osd', null, this.currentTextRightAligned ? _("Right aligned") : _("Left aligned"), "", -1, false); }, + toggleImageFile: function() { + let images = this.getImages(); + if (!images.length) + return; + if (images.length > 1) + this.currentImage = this.currentImage == images.length - 1 ? 0 : this.currentImage + 1; + this.emit('show-osd-gicon', images[this.currentImage].gicon, images[this.currentImage].toString(), "", -1, false); + }, + toggleHelp: function() { if (this.helper.visible) { this.helper.hideHelp(); @@ -1034,7 +1073,10 @@ var DrawingArea = new Lang.Class({ this._stopDrawing(); } - let content = ``; + let prefixes = 'xmlns="http://www.w3.org/2000/svg"'; + if (this.elements.some(element => element.shape == Shapes.IMAGE)) + prefixes += ' xmlns:xlink="http://www.w3.org/1999/xlink"'; + let content = ``; if (SVG_DEBUG_EXTENDS) content = ``; let backgroundColorString = this.hasBackground ? this.activeBackgroundColor.to_string() : 'transparent'; @@ -1156,7 +1198,11 @@ var DrawingArea = new Lang.Class({ return; if (contents instanceof Uint8Array) contents = ByteArray.toString(contents); - this.elements.push(...JSON.parse(contents).map(object => new Elements.DrawingElement(object))); + this.elements.push(...JSON.parse(contents).map(object => { + if (object.image) + object.image = new Files.Image(object.image); + return new Elements.DrawingElement(object); + })); if (notify) this.emit('show-osd', 'document-open-symbolic', name, "", -1, false); diff --git a/data/images/Example.svg b/data/images/Example.svg new file mode 100644 index 0000000..c2fb36c --- /dev/null +++ b/data/images/Example.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/elements.js b/elements.js index 910e897..4615e93 100644 --- a/elements.js +++ b/elements.js @@ -34,8 +34,8 @@ const reverseEnumeration = function(obj) { return reversed; }; -var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6 }; -var Manipulations = { MOVE: 100, RESIZE: 101, MIRROR: 102 }; +var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6, IMAGE: 7 }; +var ShapeNames = { 0: "Free drawing", 1: "Line", 2: "Ellipse", 3: "Rectangle", 4: "Text", 5: "Polygon", 6: "Polyline", 7: "Image" }; var Transformations = { TRANSLATION: 0, ROTATION: 1, SCALE_PRESERVE: 2, STRETCH: 3, REFLECTION: 4, INVERSION: 5 }; var LineCapNames = Object.assign(reverseEnumeration(Cairo.LineCap), { 2: 'Square' }); var LineJoinNames = reverseEnumeration(Cairo.LineJoin); @@ -46,7 +46,6 @@ var FontStyleNames = reverseEnumeration(Pango.Style); var FontStretchNames = reverseEnumeration(Pango.Stretch); var FontVariantNames = reverseEnumeration(Pango.Variant); - const SVG_DEBUG_SUPERPOSES_CAIRO = false; const RADIAN = 180 / Math.PI; // degree const INVERSION_CIRCLE_RADIUS = 12; // px @@ -58,7 +57,9 @@ const MIN_ROTATION_ANGLE = Math.PI / 1000; // rad const MIN_DRAWING_SIZE = 3; // px var DrawingElement = function(params) { - return params.shape == Shapes.TEXT ? new TextElement(params) : new _DrawingElement(params); + return params.shape == Shapes.TEXT ? new TextElement(params) : + params.shape == Shapes.IMAGE ? new ImageElement(params) : + new _DrawingElement(params); }; // DrawingElement represents a "brushstroke". @@ -109,9 +110,11 @@ const _DrawingElement = new Lang.Class({ }, buildCairo: function(cr, params) { - let [success, color] = Clutter.Color.from_string(this.color); - if (success) - Clutter.cairo_set_source_color(cr, color); + if (this.color) { + let [success, color] = Clutter.Color.from_string(this.color); + if (success) + Clutter.cairo_set_source_color(cr, color); + } if (this.showSymmetryElement) { let transformation = this.lastTransformation; @@ -726,6 +729,85 @@ const TextElement = new Lang.Class({ }, }); +const ImageElement = new Lang.Class({ + Name: 'DrawOnYourScreenImageElement', + Extends: _DrawingElement, + + _init: function(params) { + params.fill = false; + this.parent(params); + }, + + toJSON: function() { + return { + shape: this.shape, + color: this.color, + fill: this.fill, + eraser: this.eraser, + transformations: this.transformations, + image: this.image.toJson(), + preserveAspectRatio: this.preserveAspectRatio, + points: this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100]) + }; + }, + + _drawCairo: function(cr, params) { + if (this.points.length < 2) + return; + + let points = this.points; + let [x, y] = [Math.min(points[0][0], points[1][0]), Math.min(points[0][1], points[1][1])]; + let [width, height] = [Math.abs(points[1][0] - points[0][0]), Math.abs(points[1][1] - points[0][1])]; + + if (width < 1 || height < 1) + return; + + cr.save(); + this.image.setCairoSource(cr, x, y, width, height, this.preserveAspectRatio); + cr.rectangle(x, y, width, height); + cr.fill(); + cr.restore(); + + if (params.showTextRectangle) { + cr.rectangle(x, y, width, height); + setDummyStroke(cr); + } else if (params.drawTextRectangle) { + cr.rectangle(x, y, width, height); + // Only draw the rectangle to find the element, not to show it. + cr.setLineWidth(0); + } + }, + + getContainsPoint: function(cr, x, y) { + return cr.inFill(x, y); + }, + + _drawSvg: function(transAttribute) { + let points = this.points; + let row = "\n "; + let attributes = ''; + + if (points.length == 2) { + attributes = `fill="none"`; + row += ``; + } + + return row; + }, + + updateDrawing: function(x, y, transform) { + let points = this.points; + if (x == points[0][0] || y == points[0][1]) + return; + + points[1] = [x, y]; + this.preserveAspectRatio = !transform; + } +}); + const setDummyStroke = function(cr) { cr.setLineWidth(2); cr.setLineCap(0); diff --git a/extension.js b/extension.js index f4cde62..42011a6 100644 --- a/extension.js +++ b/extension.js @@ -171,6 +171,7 @@ var AreaManager = new Lang.Class({ area.leaveDrawingHandler = area.connect('leave-drawing-mode', this.toggleDrawing.bind(this)); area.updateActionModeHandler = area.connect('update-action-mode', this.updateActionMode.bind(this)); area.showOsdHandler = area.connect('show-osd', this.showOsd.bind(this)); + area.showOsdGiconHandler = area.connect('show-osd-gicon', this.showOsd.bind(this)); this.areas.push(area); } }, @@ -191,11 +192,13 @@ var AreaManager = new Lang.Class({ 'toggle-fill-rule': this.activeArea.toggleFillRule.bind(this.activeArea), 'toggle-dash' : this.activeArea.toggleDash.bind(this.activeArea), 'toggle-fill' : this.activeArea.toggleFill.bind(this.activeArea), + 'toggle-image-file' : this.activeArea.toggleImageFile.bind(this.activeArea), 'select-none-shape': () => this.activeArea.selectTool(Area.Tools.NONE), 'select-line-shape': () => this.activeArea.selectTool(Area.Tools.LINE), 'select-ellipse-shape': () => this.activeArea.selectTool(Area.Tools.ELLIPSE), 'select-rectangle-shape': () => this.activeArea.selectTool(Area.Tools.RECTANGLE), 'select-text-shape': () => this.activeArea.selectTool(Area.Tools.TEXT), + 'select-image-shape': () => this.activeArea.selectTool(Area.Tools.IMAGE), 'select-polygon-shape': () => this.activeArea.selectTool(Area.Tools.POLYGON), 'select-polyline-shape': () => this.activeArea.selectTool(Area.Tools.POLYLINE), 'select-move-tool': () => this.activeArea.selectTool(Area.Tools.MOVE), @@ -505,6 +508,7 @@ var AreaManager = new Lang.Class({ area.disconnect(area.leaveDrawingHandler); area.disconnect(area.updateActionModeHandler); area.disconnect(area.showOsdHandler); + area.disconnect(area.showOsdGiconHandler); let container = area.get_parent(); container.get_parent().remove_actor(container); container.destroy(); diff --git a/files.js b/files.js new file mode 100644 index 0000000..ec55aa7 --- /dev/null +++ b/files.js @@ -0,0 +1,132 @@ +/* jslint esversion: 6 */ + +/* + * Copyright 2019 Abakkk + * + * This file is part of DrawOnYourScreen, a drawing extension for GNOME Shell. + * https://framagit.org/abakkk/DrawOnYourScreen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +const Gdk = imports.gi.Gdk; +const GdkPixbuf = imports.gi.GdkPixbuf; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Lang = imports.lang; + +const ExtensionUtils = imports.misc.extensionUtils; +const Me = ExtensionUtils.getCurrentExtension(); +const EXAMPLE_IMAGES = Me.dir.get_child('data').get_child('images'); +const USER_IMAGES = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir'], 'images'])); + +var Image = new Lang.Class({ + Name: 'DrawOnYourScreenImage', + + _init: function(params) { + for (let key in params) + this[key] = params[key]; + }, + + toString: function() { + return this.displayName; + }, + + toJson: function() { + return { + displayName: this.displayName, + contentType: this.contentType, + _base64: this.base64, + _hash: this.hash + }; + }, + + // only called from menu so file exists + get gicon() { + if (!this._gicon) + this._gicon = new Gio.FileIcon({ file: this.file }); + return this._gicon; + }, + + get bytes() { + if (!this._bytes) { + if (this.file) + this._bytes = this.file.load_bytes(null)[0]; + else + this._bytes = new GLib.Bytes(GLib.base64_decode(this._base64)); + } + return this._bytes; + }, + + get base64() { + if (!this._base64) + this._base64 = GLib.base64_encode(this.bytes.get_data()); + return this._base64; + }, + + get hash() { + if (!this._hash) + this._hash = this.bytes.hash(); + return this._hash; + }, + + get pixbuf() { + if (!this._pixbuf) { + let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes); + this._pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null); + stream.close(null); + } + return this._pixbuf; + }, + + getPixbufAtScale: function(width, height) { + let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes); + let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, width, height, true, null); + stream.close(null); + return pixbuf; + }, + + setCairoSource: function(cr, x, y, width, height, preserveAspectRatio) { + let pixbuf = preserveAspectRatio ? this.getPixbufAtScale(width, height) + : this.pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR); + Gdk.cairo_set_source_pixbuf(cr, pixbuf, x, y); + } +}); + +var getImages = function() { + let images = []; + + [EXAMPLE_IMAGES, USER_IMAGES].forEach(directory => { + let enumerator; + try { + enumerator = directory.enumerate_children('standard::display-name,standard::content-type', Gio.FileQueryInfoFlags.NONE, null); + } catch(e) { + return; + } + + let fileInfo = enumerator.next_file(null); + while (fileInfo) { + if (fileInfo.get_content_type().indexOf('image') == 0) + images.push(new Image({ file: enumerator.get_child(fileInfo), contentType: fileInfo.get_content_type(), displayName: fileInfo.get_display_name() })); + fileInfo = enumerator.next_file(null); + } + enumerator.close(null); + }); + + images.sort((a, b) => { + return b.displayName < a.displayName; + }); + + return images; +}; diff --git a/locale/draw-on-your-screen.pot b/locale/draw-on-your-screen.pot index bb0a1dc..7ff6129 100644 --- a/locale/draw-on-your-screen.pot +++ b/locale/draw-on-your-screen.pot @@ -179,6 +179,9 @@ msgstr "" msgid "Select polyline" msgstr "" +msgid "Select image" +msgstr "" + msgid "Select text" msgstr "" @@ -227,6 +230,9 @@ msgstr "" msgid "Toggle text alignment" msgstr "" +msgid "Change image file" +msgstr "" + msgid "Hide panel and dock" msgstr "" @@ -316,6 +322,9 @@ msgstr "" msgid "Polyline" msgstr "" +msgid "Image" +msgstr "" + msgid "Move" msgstr "" diff --git a/menu.js b/menu.js index 0ecaa16..a8ccba0 100644 --- a/menu.js +++ b/menu.js @@ -190,6 +190,18 @@ var DrawingMenu = new Lang.Class({ fontSection.itemActivated = () => {}; this.fontSection = fontSection; + let imageSection = new PopupMenu.PopupMenuSection(); + let images = this.area.getImages(); + if (images.length) { + if (this.area.currentImage > images.length - 1) + this.area.currentImage = images.length - 1; + this._addSubMenuItem(imageSection, null, images, this.area, 'currentImage'); + } + this._addSeparator(imageSection); + this.menu.addMenuItem(imageSection); + imageSection.itemActivated = () => {}; + this.imageSection = imageSection; + let manager = Extension.manager; this._addSimpleSwitchItem(this.menu, _("Hide panel and dock"), manager.hiddenList ? true : false, manager.togglePanelAndDockOpacity.bind(manager)); this._addSimpleSwitchItem(this.menu, _("Add a drawing background"), this.area.hasBackground, this.area.toggleBackground.bind(this.area)); @@ -235,19 +247,13 @@ var DrawingMenu = new Lang.Class({ }, _updateSectionVisibility: function() { - if (this.area.currentTool == Area.Tools.TEXT) { - this.lineSection.actor.hide(); - this.fontSection.actor.show(); - this.colorItem.setSensitive(true); - this.fillItem.setSensitive(false); - this.fillSection.setSensitive(false); - } else { - this.lineSection.actor.show(); - this.fontSection.actor.hide(); - this.colorItem.setSensitive(true); - this.fillItem.setSensitive(true); - this.fillSection.setSensitive(true); - } + let [isText, isImage] = [this.area.currentTool == Area.Tools.TEXT, this.area.currentTool == Area.Tools.IMAGE]; + this.lineSection.actor.visible = !isText && !isImage; + this.fontSection.actor.visible = isText; + this.imageSection.actor.visible = isImage; + this.colorItem.setSensitive(!isImage); + this.fillItem.setSensitive(!isText && !isImage); + this.fillSection.setSensitive(!isText && !isImage); if (this.area.fill) this.fillSection.actor.show(); @@ -320,7 +326,9 @@ var DrawingMenu = new Lang.Class({ }, _addSubMenuItem: function(menu, icon, obj, target, targetProperty, callback) { - let item = new PopupMenu.PopupSubMenuMenuItem(_(obj[target[targetProperty]]), icon ? true : false); + if (targetProperty == 'currentImage') + icon = obj[target[targetProperty]].gicon; + let item = new PopupMenu.PopupSubMenuMenuItem(_(String(obj[target[targetProperty]])), icon ? true : false); if (icon && icon instanceof GObject.Object && GObject.type_is_a(icon, Gio.Icon)) item.icon.set_gicon(icon); else if (icon) @@ -340,12 +348,14 @@ var DrawingMenu = new Lang.Class({ else if (targetProperty == 'currentFontStyle') text = `${_(obj[i])}`; else - text = _(obj[i]); + text = _(String(obj[i])); let iCaptured = Number(i); let subItem = item.menu.addAction(text, () => { - item.label.set_text(_(obj[iCaptured])); + item.label.set_text(_(String(obj[iCaptured]))); target[targetProperty] = iCaptured; + if (targetProperty == 'currentImage') + item.icon.set_gicon(obj[iCaptured].gicon); if (callback) callback(); }); diff --git a/prefs.js b/prefs.js index 9e7df59..dc1f8c0 100644 --- a/prefs.js +++ b/prefs.js @@ -54,6 +54,7 @@ var INTERNAL_KEYBINDINGS = { 'select-polygon-shape': "Select polygon", 'select-polyline-shape': "Select polyline", 'select-text-shape': "Select text", + 'select-image-shape': "Select image", 'select-move-tool': "Select move", 'select-resize-tool': "Select resize", 'select-mirror-tool': "Select mirror", @@ -73,6 +74,7 @@ var INTERNAL_KEYBINDINGS = { 'toggle-font-weight': "Change font weight", 'toggle-font-style': "Change font style", 'toggle-text-alignment': "Toggle text alignment", + 'toggle-image-file': "Change image file", '-separator-5': '', 'toggle-panel-and-dock-visibility': "Hide panel and dock", 'toggle-background': "Add a drawing background", diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled index 4ce499444b20d3b7b8ae1ad8e742897db4aa73c6..61bc640a50075f4b049de2caf6c187d19fc0ae8a 100644 GIT binary patch literal 4428 zcmZu!Yitx%7#$P@L1B@H6si_1vQ=l>QVNA&MMWqG3D{_=QDM4!yPbA+?l?2s7mxT# z#8=W76A>kXMj(cSCO!g0BmU5+iAjGL#rUTtn8*(a8Vn}J$2oWJ&dioha`v44=9}-n zzkBb-hb`Z>+|ba^0&qu3r?1ksN#Mz2L(OEq%NT9OKJc&C7{-)&hVdVsw%ITkTMY6R zK>GPi-jxNB3A%;jWW+!y+`yJ@kjeR0zbRdFP!@eN$ofLK{MJaXQ565M{Hp;Jqc9Cj zT&pn?JPTmaHvn@1RL8gxU^}W3SO|R+a5Hd=#$qtB1h`eB7Q7U=4M+oZz#V{@KnA*u zdgumVxkeM1Xa-gSt2A1`Y+o4*TL+u@9s6*u(PtP_(GK9^_b+Xyoq8thHQ+|z;E5%Y zcIp|huLC~_^fYW#aj5CP6WkAU?Em;(`cuz_eINJ@p!#yPPdoK>upa}T0uJr`xQljb z`j3Kt29AI8$^zP{SUtVIKjH0>;80{-K?k?HU7L0v0sfbsz22j57_Bp$fQ9$DwANG`I~| zukF;dcYp_gaM!o%8Hbwf9R?o;?l0E|TGaPrDxL8F>Ei?PqDHu7Q0Wcst;1-*GqX)GXHnzXD9^JKIh> zHRt~!@Tb66;Y%OTPR(|Wg3kj_eD=9RJGEM;NIJ8DMOAO_rk#2+{1<@hfQ65i)+sgn ztp&UZNV&TQ=ugdZJHU?vb?57+(N4{N@WA_kSwm~oI-+KM4uRhTQe*zV^rvP&oCJRX z6z_WI1nty}a~6Ca`0khXA=;@qE=-uSfpXraX5O9)z63PAF!NLTQ>*orLjM6LwjNaN zrDl6;z%4-U-%ZsG@TaE#Hn0Uu`Tf#6v{Q3l4SF8oL->BJd zUx9xFj-LJ})c}8L`dNq3aS?=pDiQ08p1=|^n>(N!&&a8B# z-fIQ7HySgmlEbIVg#SHG*t)j8=n20yhn*zR`32kerEi8(I*GX13ChLh9*qym zT^|2iUM^P;sH!IS|D=CY{I99(ucuk3mF>;@vgqd6xp<=QIwz>dio|==V@2$}q8v9_ z51v7yc<^tI|23EWg>MDIjJm6$zeHt!skZSE$22n73@5szFX%nMTZl^sgvK2S9P{ z#m>;TvY}C}BIpi7w#^F@)hvA()lYwCBg?bJ6O}i%~ELx79pSsGPH@^cVVpwj$%!FbuS~|&WZOb+y5rYtsOzT_M?qp+Ev0-FJ!(fm`@zaPA@7JL zz+?U#0M{OacSEKr-bs1SVC>xUkC8cTmwu3c;{UT z@NUaJ3EPLd)9S7$t;F&DW$S+MMdduu<29tmOD~N8tHsAT#TOQW8 z^KNOuMSeQ2RT)I&3T+k a%4?w_&Y+G{K8FQ;`HJ_t-hr++-~A7^(jc$^ literal 4280 zcmai1ZHN_B7@jRHzxQgXt8S&Hc{iK8yXyLdZflvE73{JYOH?|0@7a5gJ9lQ9nY&+_ zNCkbEAW^hbk`<&z;6^4YhW4Y7An-?&5s`vH1<3}1LXteOw8RBtw%ct{t1}d+}}hy_3f}<2T#Nx!~9Y+ zzf-_hfIVAluh5@5@CUD*plRp*eV@`!Jp=8uf+di;!p`Hf&dGOD`1ATv7pg;9g*oVO>OvHoAPEGrKa0{?e*{Nx71v|jUU+=GB zJJjs=CGc^ew0_$$+Nr0*ejfY-@Q`YUn(h1wo`A{U^TNE8kF z173XZ-i!37o)7yV_$aVh`BT&XB={n5Xvc{z`cpHWVQ|%CP1~aUsp+2rHvkVSJ2mYs z;2pqLWv8ay0UrR&w(a+`eQL%(1pWp%cx<6fJN0DPFM@vuo<949Njo+3#>GD!c=Jr# z5be~Q-!ynVaOka9-lm;81$!%a5YXm+{}1idoZlnhGr+lL_e|z~rXCObdGOyr-EZ|1 z=ugc&q^4s30CS%x$4SjN8^9ZY&a`NxKQ-532ly%AwHuc|qn(=m1@K~{-z2XO4MgS%;`9tV2?ycejxwrf!kCp6<31Rn)b*6u#qskyIDf(mp^tHRr1aTnDVY|D(0EQ`6o8-UeJf zo_UUTYObpScsFqP^gk)usTt=1@EgFnE;);KYTA#0PXWW4CH+y=~>^KOt&YUZ;8EP>i>Hs`e2TH5gH zY)4pn$rC6ue!nSDi9}fFqUUTZ2l}Ed))-6!*1e|P1!Mqhv!IUDgK@-aI>0)_`ow1z z0!sj%^YZ~5hf!U^(!<k{i1>kDgG1Hd}Q`ql_61^7dnG}fMbfF>X(P-Kh3@^w>MLhqG1zYvZ8k=E_)rB2^9 zvVNS*qO?k0e7UZ@AiMnLvUTR7wQ+JCSM*4`n-iw+ zg>D+2uR}5%`fm)(NG*LlTx5OSvCaNGUJ$&3;Rrh67scDJ<|~+63$GRBuA}=wzuO!n zJ<(#;Hf@(Firc}s8{?}+ZQ7hs4{&~>cHzG)zFIbxKj$UszdXKL9{Njb^jwULYeOy< zKdEx>n3#~%;$)0I2Os7r>epBKQMn98j>Qn&$2eBRS1ZD}##$rTW$vi_t9X*jrZyQ> z&|y>D0~`}qO<{j(A3KI6Ox>_@dd|*v>pjwwozj$ke-v+FjPYQzMfcJE%J^z!*uEOR zWn;mIIjYFBGe*1Ee93lK#aF9Fv>S|)?{i82)$!HpvHaJJ@Lv=AuL=FfS|9&24_q$g z^G+odFWgQP*QQ3CC5f$omvQNMQBop55$2<4+kRoxK8^jJDdFU;4w_W`bxG6I-IB?* z68T|X9xIu?bj<$Ph|Mot>2=2zwVa|GGncd^o94HIwDM@T;&`7H_+%WzFK|-vtH%dN zeel6iAAE4s#~&Q^V|;MnIQPQnZ5)h2ePBCL{8HsZdF5T1tGJ(6_f>@apveiZ+qWIM z;g{~L^Nb#$8=mg<BAK|TAG2~?^x0`kE4sf^ z-B)cCZA?|%eH?Ew?4R!vD0f}L6;@VwLGBZAX6<6pQ2Ro-26kA*G$q%sz*`2hDXpB$ z8oupT3=HE6=aVb5SIB%JQJQ;4k1K09alG9l#wA@B9YiCG#-FWhr;B}c8AXZhS}|W; zm3|(!zl%lf3Zv4`Qhr!robafhfiNDm7u1VFbl(f_m*>*O3xHwq6|cM#B0pF81-)w8 Ef6s{ZTL1t6 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 767f859..683fbcb 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 @@ -106,6 +106,11 @@ select text select text + + ["<Primary>i"] + select image + select image + ["<Primary>p"] unselect shape (free drawing) @@ -227,7 +232,7 @@ toggle font weight - ["<Primary>i"] + ["<Primary><Shift>w"] toggle font style toggle font style @@ -236,6 +241,11 @@ toggle text alignment toggle text alignment + + ["<Primary><Shift>i"] + toggle image file + toggle image file + ["<Primary>o"] open user stylesheet to edit style