diff --git a/area.js b/area.js index d28a846..dadec04 100644 --- a/area.js +++ b/area.js @@ -367,7 +367,8 @@ var DrawingArea = new Lang.Class({ } else if (button == 2) { this.switchFill(); } else if (button == 3) { - this._stopDrawing(); + this._stopAll(); + this.menu.open(x, y); return Clutter.EVENT_STOP; } @@ -376,7 +377,8 @@ var DrawingArea = new Lang.Class({ }, _onKeyboardPopupMenu: function() { - this._stopDrawing(); + this._stopAll(); + if (this.helper.visible) this.toggleHelp(); this.menu.popup(); @@ -701,6 +703,7 @@ var DrawingArea = new Lang.Class({ }, _startWriting: function() { + let [stageX, stageY] = this.get_transformed_position(); let [x, y] = [this.currentElement.x, this.currentElement.y]; this.currentElement.text = ''; this.currentElement.cursorPosition = 0; @@ -711,16 +714,22 @@ var DrawingArea = new Lang.Class({ this.textHasCursor = true; this._redisplay(); - this.textEntry = new St.Entry({ visible: false, x, y }); - this.get_parent().add_child(this.textEntry); + // Do not hide and do not set opacity to 0 because ibusCandidatePopup need a mapped text entry to init correctly its position. + this.textEntry = new St.Entry({ opacity: 1, x: stageX + x, y: stageY + y }); + this.get_parent().insert_child_below(this.textEntry, this); this.textEntry.grab_key_focus(); this.updateActionMode(); this.updatePointerCursor(); - let ibusCandidatePopup = Main.layoutManager.uiGroup.get_children().filter(child => - child.has_style_class_name && child.has_style_class_name('candidate-popup-boxpointer'))[0] || null; + let ibusCandidatePopup = Main.layoutManager.uiGroup.get_children().find(child => + child.has_style_class_name && child.has_style_class_name('candidate-popup-boxpointer')); if (ibusCandidatePopup) { - this.ibusHandler = ibusCandidatePopup.connect('notify::visible', popup => popup.visible && (this.textEntry.visible = true)); + this.ibusHandler = ibusCandidatePopup.connect('notify::visible', () => { + if (ibusCandidatePopup.visible) { + this.get_parent().set_child_above_sibling(this.textEntry, this); + this.textEntry.opacity = 255; + } + }); this.textEntry.connect('destroy', () => ibusCandidatePopup.disconnect(this.ibusHandler)); } @@ -832,6 +841,21 @@ var DrawingArea = new Lang.Class({ }); }, + // A priori there is nothing to stop, except transformations, if there is no current element. + // 'force' argument is passed when leaving drawing mode to ensure all is clean, as a workaround for possible bugs. + _stopAll: function(force) { + if (this.grabbedElement) + this._stopTransforming(); + + if (!this.currentElement && !force) + return; + + if (this.isWriting) + this._stopWriting(); + + this._stopDrawing(); + }, + erase: function() { this.deleteLastElement(); this.elements = []; @@ -840,21 +864,8 @@ var DrawingArea = new Lang.Class({ }, deleteLastElement: function() { - if (this.currentElement) { - if (this.motionHandler) { - this.disconnect(this.motionHandler); - this.motionHandler = null; - } - if (this.buttonReleasedHandler) { - this.disconnect(this.buttonReleasedHandler); - this.buttonReleasedHandler = null; - } - if (this.isWriting) - this._stopWriting(); - this.currentElement = null; - } else { - this.elements.pop(); - } + this._stopAll(); + this.elements.pop(); this._redisplay(); }, @@ -1085,25 +1096,16 @@ var DrawingArea = new Lang.Class({ this.disconnect(this.keyboardPopupMenuHandler); this.keyboardPopupMenuHandler = null; } - if (this.motionHandler) { - this.disconnect(this.motionHandler); - this.motionHandler = null; - } - if (this.buttonReleasedHandler) { - this.disconnect(this.buttonReleasedHandler); - this.buttonReleasedHandler = null; - } if (this.scrollHandler) { this.disconnect(this.scrollHandler); this.scrollHandler = null; } - this.currentElement = null; - this._stopTextCursorTimeout(); + this._stopAll(true); + if (erase) this.erase(); - else - this._redisplay(); + this.closeMenu(); this.get_parent().set_background_color(null); Files.Images.reset(); @@ -1143,12 +1145,7 @@ var DrawingArea = new Lang.Class({ }, exportToSvg: function() { - // stop drawing or writing - if (this.currentElement && this.currentElement.shape == Shapes.TEXT && this.isWriting) { - this._stopWriting(); - } else if (this.currentElement && this.currentElement.shape != Shapes.TEXT) { - this._stopDrawing(); - } + this._stopAll(); let prefixes = 'xmlns="http://www.w3.org/2000/svg"'; if (this.elements.some(element => element.shape == Shapes.IMAGE)) @@ -1181,12 +1178,7 @@ var DrawingArea = new Lang.Class({ }, _saveAsJson: function(json, notify, callback) { - // stop drawing or writing - if (this.currentElement && this.currentElement.shape == Shapes.TEXT && this.isWriting) { - this._stopWriting(); - } else if (this.currentElement && this.currentElement.shape != Shapes.TEXT) { - this._stopDrawing(); - } + this._stopAll(); // do not use "content = JSON.stringify(this.elements, null, 2);", neither "content = JSON.stringify(this.elements);" // do compromise between disk usage and human readability @@ -1225,12 +1217,8 @@ var DrawingArea = new Lang.Class({ }, _loadJson: function(json, notify) { - // stop drawing or writing - if (this.currentElement && this.currentElement.shape == Shapes.TEXT && this.isWriting) { - this._stopWriting(); - } else if (this.currentElement && this.currentElement.shape != Shapes.TEXT) { - this._stopDrawing(); - } + this._stopAll(); + this.elements = []; this.currentElement = null; diff --git a/elements.js b/elements.js index b89800d..82338dd 100644 --- a/elements.js +++ b/elements.js @@ -670,9 +670,9 @@ const TextElement = new Lang.Class({ layout.set_font_description(this.font); layout.set_text(this.text, -1); this.textWidth = layout.get_pixel_size()[0]; - cr.moveTo(this.x, this.y - layout.get_baseline() / Pango.SCALE); + cr.moveTo(this.x, this.y); layout.set_text(this.text, -1); - PangoCairo.show_layout(cr, layout); + PangoCairo.show_layout_line(cr, layout.get_line(0)); if (params.showTextCursor) { let cursorPosition = this.cursorPosition == -1 ? this.text.length : this.cursorPosition; diff --git a/files.js b/files.js index 22a31bc..c91b404 100644 --- a/files.js +++ b/files.js @@ -318,11 +318,12 @@ var Images = { }, addImagesFromClipboard: function(callback) { - Clipboard.get_text(CLIPBOARD_TYPE, (clipBoard, text) => { + Clipboard.get_text(CLIPBOARD_TYPE, (clipboard, text) => { if (!text) return; - - let lines = text.split('\n'); + + // Since 3.38 there is a line terminator character, that has to be removed with .trim(). + let lines = text.split('\n').map(line => line.trim()); if (lines[0] == 'x-special/nautilus-clipboard') lines = lines.slice(2); @@ -443,7 +444,9 @@ var Jsons = { return; let directory = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir']])); - this._monitor = directory.monitor(Gio.FileMonitorFlags.NONE, null); + // It is important to specify that the file to monitor is a directory because maybe the directory does not exist yet + // and remove events would not be monitored. + this._monitor = directory.monitor_directory(Gio.FileMonitorFlags.NONE, null); this._monitorHandler = this._monitor.connect('changed', (monitor, file) => { if (file.get_basename() != `${Me.metadata['persistent-file-name']}.json` && file.get_basename().indexOf('.goutputstream')) this.reset(); diff --git a/helper.js b/helper.js index 444ff93..fdeeb10 100644 --- a/helper.js +++ b/helper.js @@ -21,13 +21,13 @@ * along with this program. If not, see . */ +const Clutter = imports.gi.Clutter; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const St = imports.gi.St; const Config = imports.misc.config; const ExtensionUtils = imports.misc.extensionUtils; -const Tweener = imports.ui.tweener; const Me = ExtensionUtils.getCurrentExtension(); const Convenience = ExtensionUtils.getSettings ? ExtensionUtils : Me.imports.convenience; @@ -35,6 +35,7 @@ const Shortcuts = Me.imports.shortcuts; const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext; const GS_VERSION = Config.PACKAGE_VERSION; +const Tweener = GS_VERSION < '3.33.0' ? imports.ui.tweener : null; const HELPER_ANIMATION_TIME = 0.25; const MEDIA_KEYS_SCHEMA = 'org.gnome.settings-daemon.plugins.media-keys'; @@ -175,20 +176,33 @@ var DrawingHelper = new Lang.Class({ else this.vscrollbar_policy = Gtk.PolicyType.NEVER; - Tweener.removeTweens(this); - Tweener.addTween(this, { opacity: 255, - time: HELPER_ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: null }); + if (Tweener) { + Tweener.removeTweens(this); + Tweener.addTween(this, { opacity: 255, + time: HELPER_ANIMATION_TIME, + transition: 'easeOutQuad' }); + } else { + this.remove_all_transitions(); + this.ease({ opacity: 255, + duration: HELPER_ANIMATION_TIME * 1000, + transition: Clutter.AnimationMode.EASE_OUT_QUAD }); + } }, hideHelp: function() { - Tweener.removeTweens(this); - Tweener.addTween(this, { opacity: 0, - time: HELPER_ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: this.hide.bind(this) }); - + if (Tweener) { + Tweener.removeTweens(this); + Tweener.addTween(this, { opacity: 0, + time: HELPER_ANIMATION_TIME, + transition: 'easeOutQuad', + onComplete: this.hide.bind(this) }); + } else { + this.remove_all_transitions(); + this.ease({ opacity: 0, + duration: HELPER_ANIMATION_TIME * 1000, + transition: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: this.hide.bind(this) }); + } } }); diff --git a/locale/draw-on-your-screen.pot b/locale/draw-on-your-screen.pot index e94b500..3b2aa41 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-17 22:27+0200\n" +"POT-Creation-Date: 2020-09-18 11:37+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -284,6 +284,12 @@ msgstr "" msgid "Color" msgstr "" +msgid "Add to images" +msgstr "" + +msgid "Delete" +msgstr "" + msgid "Type a name" msgstr "" @@ -417,7 +423,7 @@ msgstr "" msgid "Smooth free drawing outline" msgstr "" -msgid "Unlock image ratio" +msgid "Do not preserve image ratio" msgstr "" msgid "Rotate (while moving)" diff --git a/menu.js b/menu.js index 86a6032..53364bf 100644 --- a/menu.js +++ b/menu.js @@ -616,29 +616,24 @@ var DrawingMenu = new Lang.Class({ }); getActor(subItem).add_child(expander); - let insertButton = new St.Button({ style_class: 'button draw-on-your-screen-menu-inline-button', - child: new St.Icon({ icon_name: 'insert-image-symbolic', - style_class: 'popup-menu-icon' }) }); - getActor(subItem).add_child(insertButton); - - insertButton.connect('clicked', () => { + let insertCallback = () => { this.area.currentImage = json.image; this.imageItem.update(); this.area.currentTool = this.drawingTools.IMAGE; this.toolItem.update(); this._updateSectionVisibility(); - }); + }; + let insertButton = new ActionButton(_("Add to images"), 'insert-image-symbolic', insertCallback, null, true); + getActor(subItem).add_child(insertButton); - let deleteButton = new St.Button({ style_class: 'button draw-on-your-screen-menu-inline-button draw-on-your-screen-menu-destructive-button', - child: new St.Icon({ icon_name: 'edit-delete-symbolic', - style_class: 'popup-menu-icon' }) }); - getActor(subItem).add_child(deleteButton); - - deleteButton.connect('clicked', () => { + let deleteCallback = () => { json.delete(); subItem.destroy(); this.openDrawingSubMenuItem.setSensitive(!this.openDrawingSubMenu.isEmpty()); - }); + }; + let deleteButton = new ActionButton(_("Delete"), 'edit-delete-symbolic', deleteCallback, null, true); + deleteButton.child.add_style_class_name('draw-on-your-screen-menu-destructive-button'); + getActor(subItem).add_child(deleteButton); }); this.openDrawingSubMenuItem.setSensitive(!this.openDrawingSubMenu.isEmpty()); @@ -722,16 +717,18 @@ const ActionButton = new Lang.Class({ hideLabel: Dash.DashItemContainer.prototype.hideLabel, _syncLabel: Dash.Dash.prototype._syncLabel, - _init: function(name, icon, callback, callbackAfter) { + _init: function(name, icon, callback, callbackAfter, inline) { this._labelText = name; let button = new St.Button({ track_hover: true, x_align: Clutter.ActorAlign.CENTER, accessible_name: name, - // use 'popup-menu' and 'popup-menu-item' style classes to provide theme colors - //style_class: 'system-menu-action popup-menu-item popup-menu' }); - style_class: 'button draw-on-your-screen-menu-action-button' }); + style_class: `button draw-on-your-screen-menu-${inline ? 'inline' : 'action'}-button` }); + button.child = new St.Icon(typeof icon == 'string' ? { icon_name: icon } : { gicon: icon }); + if (inline) + button.child.add_style_class_name('popup-menu-icon'); + button.connect('clicked', () => { callback(); if (callbackAfter) @@ -740,7 +737,7 @@ const ActionButton = new Lang.Class({ button.bind_property('reactive', button, 'can_focus', GObject.BindingFlags.DEFAULT); button.connect('notify::hover', () => this._syncLabel(this)); - this.parent({ child: button, x_expand: true }); + this.parent({ child: button, x_expand: inline ? false : true }); }, get label() { diff --git a/shortcuts.js b/shortcuts.js index 488485a..df753d4 100644 --- a/shortcuts.js +++ b/shortcuts.js @@ -83,7 +83,7 @@ const getOthers = function() { [_("Extend circle to ellipse"), getKeyLabel('')], [_("Curve line"), getKeyLabel('')], [_("Smooth free drawing outline"), getKeyLabel('')], - [_("Unlock image ratio"), getKeyLabel('')], + [_("Do not preserve image ratio"), getKeyLabel('')], [_("Rotate (while moving)"), getKeyLabel('')], [_("Stretch (while resizing)"), getKeyLabel('')], [_("Inverse (while mirroring)"), getKeyLabel('')],