diff --git a/area.js b/area.js index fedd40f..56abb52 100644 --- a/area.js +++ b/area.js @@ -73,8 +73,7 @@ const getClutterColorFromString = function(string, fallback) { 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] }, + Signals: { 'show-osd': { param_types: [Gio.Icon.$gtype, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_DOUBLE, GObject.TYPE_BOOLEAN] }, 'update-action-mode': {}, 'leave-drawing-mode': {} }, @@ -198,10 +197,10 @@ var DrawingArea = new Lang.Class({ get fontFamilies() { if (!this._fontFamilies) { - let pangoFontFamilies = Elements.getPangoFontFamilies().filter(family => { + let otherFontFamilies = Elements.getAllFontFamilies().filter(family => { return family != this.defaultFontFamily && FontGenericFamilies.indexOf(family) == -1; }); - this._fontFamilies = [this.defaultFontFamily].concat(FontGenericFamilies, pangoFontFamilies); + this._fontFamilies = [this.defaultFontFamily].concat(FontGenericFamilies, otherFontFamilies); } return this._fontFamilies; }, @@ -995,7 +994,7 @@ var DrawingArea = new Lang.Class({ 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); + this.emit('show-osd', images[this.currentImage].gicon, images[this.currentImage].toString(), "", -1, false); }, pasteImageFiles: function() { @@ -1003,7 +1002,7 @@ var DrawingArea = new Lang.Class({ this.currentImage = index; this.currentTool = Shapes.IMAGE; this.updatePointerCursor(); - this.emit('show-osd-gicon', images[this.currentImage].gicon, images[this.currentImage].toString(), "", -1, false); + this.emit('show-osd', images[this.currentImage].gicon, images[this.currentImage].toString(), "", -1, false); }); }, @@ -1168,7 +1167,7 @@ var DrawingArea = new Lang.Class({ GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { json.contents = contents; if (notify) - this.emit('show-osd', 'document-save-symbolic', name, "", -1, false); + this.emit('show-osd', Files.Icons.SAVE, name, "", -1, false); if (name != Me.metadata['persistent-file-name']) { this.jsonName = name; this.lastJsonContents = contents; @@ -1220,7 +1219,7 @@ var DrawingArea = new Lang.Class({ })); if (notify) - this.emit('show-osd', 'document-open-symbolic', name, "", -1, false); + this.emit('show-osd', Files.Icons.OPEN, name, "", -1, false); if (name != Me.metadata['persistent-file-name']) { this.jsonName = name; this.lastJsonContents = contents; @@ -1236,26 +1235,26 @@ var DrawingArea = new Lang.Class({ this._redisplay(); }, - loadNextJson: function() { - let names = Files.getJsons().map(json => json.name); - - if (!names.length) - return; - - let nextName = names[this.jsonName && names.indexOf(this.jsonName) != names.length - 1 ? names.indexOf(this.jsonName) + 1 : 0]; - this.loadJson(nextName, true); - }, - loadPreviousJson: function() { let names = Files.getJsons().map(json => json.name); if (!names.length) return; - let previousName = names[this.jsonName && names.indexOf(this.jsonName) > 0 ? names.indexOf(this.jsonName) - 1 : names.length - 1]; + let previousName = names[this.jsonName && names.indexOf(this.jsonName) != names.length - 1 ? names.indexOf(this.jsonName) + 1 : 0]; this.loadJson(previousName, true); }, + loadNextJson: function() { + let names = Files.getJsons().map(json => json.name); + + if (!names.length) + return; + + let nextName = names[this.jsonName && names.indexOf(this.jsonName) > 0 ? names.indexOf(this.jsonName) - 1 : names.length - 1]; + this.loadJson(nextName, true); + }, + get drawingContentsHasChanged() { let contents = `[\n ` + new Array(...this.elements.map(element => JSON.stringify(element))).join(`,\n\n `) + `\n]`; return contents != this.lastJsonContents; diff --git a/elements.js b/elements.js index 3191dfb..9be96ff 100644 --- a/elements.js +++ b/elements.js @@ -1,3 +1,6 @@ +/* jslint esversion: 6 */ +/* exported Shapes, Transformations, getAllFontFamilies, DrawingElement */ + /* * Copyright 2019 Abakkk * @@ -18,9 +21,6 @@ * along with this program. If not, see . */ -/* jslint esversion: 6 */ -/* exported Shapes, Transformations, getPangoFontFamilies, DrawingElement */ - const Cairo = imports.cairo; const Clutter = imports.gi.Clutter; const Lang = imports.lang; @@ -30,7 +30,7 @@ const PangoCairo = imports.gi.PangoCairo; var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6, IMAGE: 7 }; var Transformations = { TRANSLATION: 0, ROTATION: 1, SCALE_PRESERVE: 2, STRETCH: 3, REFLECTION: 4, INVERSION: 5 }; -var getPangoFontFamilies = function() { +var getAllFontFamilies = function() { return PangoCairo.font_map_get_default().list_families().map(fontFamily => fontFamily.get_name()).sort((a,b) => a.localeCompare(b)); }; @@ -80,12 +80,13 @@ const _DrawingElement = new Lang.Class({ if (params.transformations === undefined) this.transformations = []; - if (params.font && params.font.weight === 0) - this.font.weight = 400; - if (params.font && params.font.weight === 1) - this.font.weight = 700; + if (params.font && typeof params.font != 'string') { // compatibility with v6.2- + if (params.font.weight === 0) + this.font.weight = 400; + else if (params.font.weight === 1) + this.font.weight = 700; let font = new Pango.FontDescription(); ['family', 'weight', 'style', 'stretch', 'variant'].forEach(attribute => { if (params.font[attribute] !== undefined) diff --git a/extension.js b/extension.js index d838cf9..ec5a10e 100644 --- a/extension.js +++ b/extension.js @@ -21,7 +21,6 @@ * along with this program. If not, see . */ -const Gio = imports.gi.Gio; const Lang = imports.lang; const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; @@ -36,6 +35,7 @@ const PanelMenu = imports.ui.panelMenu; const Me = ExtensionUtils.getCurrentExtension(); const Convenience = ExtensionUtils.getSettings && ExtensionUtils.initTranslations ? ExtensionUtils : Me.imports.convenience; const Area = Me.imports.area; +const Files = Me.imports.files; const Helper = Me.imports.helper; const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext; @@ -86,8 +86,6 @@ const AreaManager = new Lang.Class({ _init: function() { this.areas = []; this.activeArea = null; - this.enterGicon = new Gio.ThemedIcon({ name: 'applications-graphics-symbolic' }); - this.leaveGicon = new Gio.ThemedIcon({ name: 'application-exit-symbolic' }); Main.wm.addKeybinding('toggle-drawing', Me.settings, @@ -164,7 +162,6 @@ const 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); } }, @@ -344,7 +341,7 @@ const AreaManager = new Lang.Class({ Main.popModal(this.activeArea); if (source && source == global.display) // Translators: "released" as the opposite of "grabbed" - this.showOsd(null, 'touchpad-disabled-symbolic', _("Keyboard and pointer released"), null, null, false); + this.showOsd(null, Files.Icons.UNGRAB, _("Keyboard and pointer released"), null, null, false); this.setCursor('DEFAULT'); this.activeArea.reactive = false; this.removeInternalKeybindings(); @@ -357,7 +354,7 @@ const AreaManager = new Lang.Class({ this.activeArea.reactive = true; this.activeArea.initPointerCursor(); if (source && source == global.display) - this.showOsd(null, 'input-touchpad-symbolic', _("Keyboard and pointer grabbed"), null, null, false); + this.showOsd(null, Files.Icons.GRAB, _("Keyboard and pointer grabbed"), null, null, false); } return true; @@ -368,7 +365,7 @@ const AreaManager = new Lang.Class({ let activeIndex = this.areas.indexOf(this.activeArea); let save = activeIndex == Main.layoutManager.primaryIndex && Me.settings.get_boolean('persistent-drawing'); - this.showOsd(null, this.leaveGicon, _("Leaving drawing mode")); + this.showOsd(null, Files.Icons.LEAVE, _("Leaving drawing mode")); this.activeArea.leaveDrawingMode(save); if (this.hiddenList) this.togglePanelAndDockOpacity(); @@ -392,7 +389,7 @@ const AreaManager = new Lang.Class({ this.osdDisabled = Me.settings.get_boolean('osd-disabled'); // Translators: %s is a key label let label = "" + _("Press %s for help").format(this.activeArea.helper.helpKeyLabel) + "\n\n" + _("Entering drawing mode"); - this.showOsd(null, this.enterGicon, label, null, null, true); + this.showOsd(null, Files.Icons.ENTER, label, null, null, true); } if (this.indicator) @@ -426,10 +423,8 @@ const AreaManager = new Lang.Class({ if (level && GS_VERSION > '3.33.0') level = level / 100; - if (icon && typeof icon == 'string') - icon = new Gio.ThemedIcon({ name: icon }); - else if (!icon) - icon = this.enterGicon; + if (!icon) + icon = Files.Icons.ENTER; let osdWindow = Main.osdWindowManager._osdWindows[activeIndex]; @@ -497,7 +492,6 @@ const 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 index b345c16..867d1ff 100644 --- a/files.js +++ b/files.js @@ -1,5 +1,5 @@ /* jslint esversion: 6 */ -/* exported Image, Images, Json, getJsons, getDateString */ +/* exported Icons, Image, Images, Json, getJsons, getDateString */ /* * Copyright 2019 Abakkk @@ -35,6 +35,29 @@ 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'])); const Clipboard = St.Clipboard.get_default(); const CLIPBOARD_TYPE = St.ClipboardType.CLIPBOARD; +const ICON_DIR = Me.dir.get_child('data').get_child('icons'); +const ICON_NAMES = ['color', 'dashed-line', 'fillrule-evenodd', 'fillrule-nonzero', 'fill', 'full-line', 'linecap', 'linejoin', 'palette', 'smooth', 'stroke']; + +var Icons = { + get ENTER() { return this._enter || void (this._enter = new Gio.ThemedIcon({ name: 'applications-graphics-symbolic' })) || this._enter; }, + get GRAB() { return this._grab || void (this._grab = new Gio.ThemedIcon({ name: 'input-touchpad-symbolic' })) || this._grab; }, + get LEAVE() { return this._leave || void (this._leave = new Gio.ThemedIcon({ name: 'application-exit-symbolic' })) || this._leave; }, + get OPEN() { return this._open || void (this._open = new Gio.ThemedIcon({ name: 'document-open-symbolic' })) || this._open; }, + get SAVE() { return this._save || void (this._save = new Gio.ThemedIcon({ name: 'document-save-symbolic' })) || this._save; }, + get UNGRAB() { return this._ungrab || void (this._ungrab = new Gio.ThemedIcon({ name: 'touchpad-disabled-symbolic' })) || this._ungrab; } +}; + +ICON_NAMES.forEach(name => { + Object.defineProperty(Icons, name.toUpperCase().replace(/-/gi, '_'), { + get: function() { + if (!this[`_${name}`]) { + let file = Gio.File.new_for_path(ICON_DIR.get_child(`${name}-symbolic.svg`).get_path()); + this[`_${name}`] = file.query_exists(null) ? new Gio.FileIcon({ file }) : new Gio.ThemedIcon({ name: 'error-symbolic' }); + } + return this[`_${name}`]; + } + }); +}); // wrapper around an image file var Image = new Lang.Class({ diff --git a/menu.js b/menu.js index 529667f..5d28811 100644 --- a/menu.js +++ b/menu.js @@ -42,20 +42,6 @@ const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext; const pgettext = imports.gettext.domain(Me.metadata['gettext-domain']).pgettext; const GS_VERSION = Config.PACKAGE_VERSION; - -const ICON_DIR = Me.dir.get_child('data').get_child('icons'); -const SMOOTH_ICON_PATH = ICON_DIR.get_child('smooth-symbolic.svg').get_path(); -const PALETTE_ICON_PATH = ICON_DIR.get_child('palette-symbolic.svg').get_path(); -const COLOR_ICON_PATH = ICON_DIR.get_child('color-symbolic.svg').get_path(); -const FILL_ICON_PATH = ICON_DIR.get_child('fill-symbolic.svg').get_path(); -const STROKE_ICON_PATH = ICON_DIR.get_child('stroke-symbolic.svg').get_path(); -const LINEJOIN_ICON_PATH = ICON_DIR.get_child('linejoin-symbolic.svg').get_path(); -const LINECAP_ICON_PATH = ICON_DIR.get_child('linecap-symbolic.svg').get_path(); -const FILLRULE_NONZERO_ICON_PATH = ICON_DIR.get_child('fillrule-nonzero-symbolic.svg').get_path(); -const FILLRULE_EVENODD_ICON_PATH = ICON_DIR.get_child('fillrule-evenodd-symbolic.svg').get_path(); -const DASHED_LINE_ICON_PATH = ICON_DIR.get_child('dashed-line-symbolic.svg').get_path(); -const FULL_LINE_ICON_PATH = ICON_DIR.get_child('full-line-symbolic.svg').get_path(); - // 150 labels with font-family style take ~15Mo const FONT_FAMILY_STYLE = true; // use 'login-dialog-message-warning' class in order to get GS theme warning color (default: #f57900) @@ -185,18 +171,6 @@ var DrawingMenu = new Lang.Class({ this.saveDrawingSubMenu.close(); menuCloseFunc.bind(this.menu)(animate); }; - - this.paletteIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(PALETTE_ICON_PATH) }); - this.colorIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(COLOR_ICON_PATH) }); - this.smoothIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(SMOOTH_ICON_PATH) }); - this.strokeIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(STROKE_ICON_PATH) }); - this.fillIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FILL_ICON_PATH) }); - this.fillRuleNonzeroIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FILLRULE_NONZERO_ICON_PATH) }); - this.fillRuleEvenoddIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FILLRULE_EVENODD_ICON_PATH) }); - this.linejoinIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(LINEJOIN_ICON_PATH) }); - this.linecapIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(LINECAP_ICON_PATH) }); - this.fullLineIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FULL_LINE_ICON_PATH) }); - this.dashedLineIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(DASHED_LINE_ICON_PATH) }); }, disable: function() { @@ -253,25 +227,25 @@ var DrawingMenu = new Lang.Class({ getActor(groupItem).add_child(this._createActionButton(_("Undo"), this.area.undo.bind(this.area), 'edit-undo-symbolic')); getActor(groupItem).add_child(this._createActionButton(_("Redo"), this.area.redo.bind(this.area), 'edit-redo-symbolic')); getActor(groupItem).add_child(this._createActionButton(_("Erase"), this.area.deleteLastElement.bind(this.area), 'edit-clear-all-symbolic')); - getActor(groupItem).add_child(this._createActionButton(_("Smooth"), this.area.smoothLastElement.bind(this.area), this.smoothIcon)); + getActor(groupItem).add_child(this._createActionButton(_("Smooth"), this.area.smoothLastElement.bind(this.area), Files.Icons.SMOOTH)); this.menu.addMenuItem(groupItem); this._addSeparator(this.menu, true); this._addSubMenuItem(this.menu, 'document-edit-symbolic', DisplayStrings.Tool, this.area, 'currentTool', this._updateSectionVisibility.bind(this)); this.paletteItem = this._addPaletteSubMenuItem(this.menu); this.colorItem = this._addColorSubMenuItem(this.menu); - this.fillItem = this._addSwitchItem(this.menu, DisplayStrings.getFill(true), this.strokeIcon, this.fillIcon, this.area, 'fill', this._updateSectionVisibility.bind(this)); + this.fillItem = this._addSwitchItem(this.menu, DisplayStrings.getFill(true), Files.Icons.STROKE, Files.Icons.FILL, this.area, 'fill', this._updateSectionVisibility.bind(this)); this.fillSection = new PopupMenu.PopupMenuSection(); this.fillSection.itemActivated = () => {}; - this.fillRuleItem = this._addSwitchItem(this.fillSection, DisplayStrings.FillRule[1], this.fillRuleNonzeroIcon, this.fillRuleEvenoddIcon, this.area, 'currentEvenodd'); + this.fillRuleItem = this._addSwitchItem(this.fillSection, DisplayStrings.FillRule[1], Files.Icons.FILLRULE_NONZERO, Files.Icons.FILLRULE_EVENODD, this.area, 'currentEvenodd'); this.menu.addMenuItem(this.fillSection); this._addSeparator(this.menu); let lineSection = new PopupMenu.PopupMenuSection(); this._addSliderItem(lineSection, this.area, 'currentLineWidth'); - this._addSubMenuItem(lineSection, this.linejoinIcon, DisplayStrings.LineJoin, this.area, 'currentLineJoin'); - this._addSubMenuItem(lineSection, this.linecapIcon, DisplayStrings.LineCap, this.area, 'currentLineCap'); - this._addSwitchItem(lineSection, DisplayStrings.getDashedLine(true), this.fullLineIcon, this.dashedLineIcon, this.area, 'dashedLine'); + this._addSubMenuItem(lineSection, Files.Icons.LINEJOIN, DisplayStrings.LineJoin, this.area, 'currentLineJoin'); + this._addSubMenuItem(lineSection, Files.Icons.LINECAP, DisplayStrings.LineCap, this.area, 'currentLineCap'); + this._addSwitchItem(lineSection, DisplayStrings.getDashedLine(true), Files.Icons.FULL_LINE, Files.Icons.DASHED_LINE, this.area, 'dashedLine'); this._addSeparator(lineSection); this.menu.addMenuItem(lineSection); lineSection.itemActivated = () => {}; @@ -473,7 +447,7 @@ var DrawingMenu = new Lang.Class({ _addPaletteSubMenuItem: function(menu) { let text = _(this.area.currentPalette[0] || "Palette"); let item = new PopupMenu.PopupSubMenuMenuItem(text, true); - item.icon.set_gicon(this.paletteIcon); + item.icon.set_gicon(Files.Icons.PALETTE); item.menu.itemActivated = () => { item.menu.close(); @@ -501,7 +475,7 @@ var DrawingMenu = new Lang.Class({ _addColorSubMenuItem: function(menu) { let item = new PopupMenu.PopupSubMenuMenuItem(_("Color"), true); this.colorSubMenu = item.menu; - item.icon.set_gicon(this.colorIcon); + item.icon.set_gicon(Files.Icons.COLOR); item.icon.set_style(`color:${this.area.currentColor.to_string().slice(0, 7)};`); item.menu.itemActivated = () => {