From f18e8e6ac047f37c684a92ca9830eeddafcc5958 Mon Sep 17 00:00:00 2001 From: abakkk Date: Sun, 6 Dec 2020 13:43:42 +0100 Subject: [PATCH 01/12] add toggle animations * background * grid * square area --- area.js | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/area.js b/area.js index 93d54ed..14b44cf 100644 --- a/area.js +++ b/area.js @@ -48,6 +48,7 @@ const pgettext = imports.gettext.domain(Me.metadata['gettext-domain']).pgettext; const MOTION_TIME = 1; // ms, time accuracy for free drawing, max is about 33 ms. The lower it is, the smoother the drawing is. const TEXT_CURSOR_TIME = 600; // ms const ELEMENT_GRABBER_TIME = 80; // ms, default is about 16 ms +const TOGGLE_ANIMATION_DURATION = 300; // ms const GRID_TILES_HORIZONTAL_NUMBER = 30; const COLOR_PICKER_EXTENSION_UUID = 'color-picker@tuberry'; const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_'); @@ -143,6 +144,7 @@ var DrawingArea = new Lang.Class({ this.layerContainer.add_child(this.foreLayer); this.gridLayer = new DrawingLayer(this._repaintGrid.bind(this)); this.gridLayer.hide(); + this.gridLayer.opacity = 0; this.layerContainer.add_child(this.gridLayer); this.elements = []; @@ -1015,7 +1017,16 @@ var DrawingArea = new Lang.Class({ toggleBackground: function() { this.hasBackground = !this.hasBackground; - this.set_background_color(this.hasBackground ? this.areaBackgroundColor : null); + let backgroundColor = this.hasBackground ? this.areaBackgroundColor : Clutter.Color.get_static(Clutter.StaticColor.TRANSPARENT); + + if (this.ease) { + this.remove_all_transitions(); + this.ease({ backgroundColor, + duration: TOGGLE_ANIMATION_DURATION, + transition: Clutter.AnimationMode.EASE_IN_OUT_QUAD }); + } else { + this.set_background_color(backgroundColor); + } }, get hasGrid() { @@ -1024,19 +1035,43 @@ var DrawingArea = new Lang.Class({ toggleGrid: function() { // The grid layer is repainted when the visibility changes. - this.gridLayer.visible = !this.gridLayer.visible; + if (this.gridLayer.ease) { + this.gridLayer.remove_all_transitions(); + let visible = !this.gridLayer.visible; + this.gridLayer.visible = true; + this.gridLayer.ease({ opacity: visible ? 255 : 0, + duration: TOGGLE_ANIMATION_DURATION, + transition: Clutter.AnimationMode.EASE_IN_OUT_QUAD, + onStopped: () => this.gridLayer.visible = visible }); + } else { + this.gridLayer.visible = !this.gridLayer.visible; + } }, toggleSquareArea: function() { this.isSquareArea = !this.isSquareArea; + let x, y, width, height, onComplete; + if (this.isSquareArea) { - this.layerContainer.set_position((this.monitor.width - this.squareAreaSize) / 2, (this.monitor.height - this.squareAreaSize) / 2); - this.layerContainer.set_size(this.squareAreaSize, this.squareAreaSize); this.layerContainer.add_style_class_name('draw-on-your-screen-square-area'); + [x, y] = [(this.monitor.width - this.squareAreaSize) / 2, (this.monitor.height - this.squareAreaSize) / 2]; + width = height = this.squareAreaSize; + onComplete = () => {}; } else { - this.layerContainer.set_position(0, 0); - this.layerContainer.set_size(this.monitor.width, this.monitor.height); - this.layerContainer.remove_style_class_name('draw-on-your-screen-square-area'); + x = y = 0; + [width, height] = [this.monitor.width, this.monitor.height]; + onComplete = () => this.layerContainer.remove_style_class_name('draw-on-your-screen-square-area'); + } + + if (this.layerContainer.ease) { + this.layerContainer.remove_all_transitions(); + this.layerContainer.ease({ x, y, width, height, onComplete, + duration: TOGGLE_ANIMATION_DURATION, + transition: Clutter.AnimationMode.EASE_OUT_QUAD }); + } else { + this.layerContainer.set_position(x, y); + this.layerContainer.set_size(width, height); + onComplete(); } }, From 5698f3f7cb86dd4bf6737cc7900f017be026a681 Mon Sep 17 00:00:00 2001 From: abakkk Date: Wed, 17 Feb 2021 11:33:32 +0100 Subject: [PATCH 02/12] multi-line text elements * Can browse and break lines anywhere * Can past multi-line texts * Do not need lineIndex anymore and grouped lines are preserved permanently Related to #56 --- area.js | 73 +++++++++++----------------- elements.js | 134 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 106 insertions(+), 101 deletions(-) diff --git a/area.js b/area.js index 14b44cf..d447ae7 100644 --- a/area.js +++ b/area.js @@ -813,7 +813,9 @@ var DrawingArea = new Lang.Class({ this.textHasCursor = true; this._redisplay(); - // Do not hide and do not set opacity to 0 because ibusCandidatePopup need a mapped text entry to init correctly its position. + // Do not hide and do not set opacity to 0 because: + // 1. ibusCandidatePopup need a mapped text entry to init correctly its position. + // 2. 'cursor-changed' signal is no emitted if the text entry is not visible. this.textEntry = new St.Entry({ opacity: 1, x: stageX + x, y: stageY + y }); this.insert_child_below(this.textEntry, null); this.textEntry.grab_key_focus(); @@ -832,17 +834,26 @@ var DrawingArea = new Lang.Class({ this.textEntry.connect('destroy', () => ibusCandidatePopup.disconnect(this.ibusHandler)); } + this.textEntry.clutterText.set_single_line_mode(false); this.textEntry.clutterText.connect('activate', (clutterText) => { this._stopWriting(); }); - this.textEntry.clutterText.connect('text-changed', (clutterText) => { - GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - this.currentElement.text = clutterText.text; - this.currentElement.cursorPosition = clutterText.cursorPosition; - this._updateTextCursorTimeout(); - this._redisplay(); - }); + let showCursorOnPositionChanged = true; + this.textEntry.clutterText.connect('text-changed', clutterText => { + this.textEntry.y = stageY + y + (this.textEntry.clutterText.get_layout().get_line_count() - 1) * this.currentElement.height; + this.currentElement.text = clutterText.text; + showCursorOnPositionChanged = false; + this._redisplay(); + }); + + this.textEntry.clutterText.connect('cursor-changed', clutterText => { + this.currentElement.cursorPosition = clutterText.cursorPosition; + this._updateTextCursorTimeout(); + let cursorPosition = clutterText.cursorPosition == -1 ? clutterText.text.length : clutterText.cursorPosition; + this.textHasCursor = showCursorOnPositionChanged || GLib.unichar_isspace(clutterText.text.charAt(cursorPosition - 1)); + showCursorOnPositionChanged = true; + this._redisplay(); }); this.textEntry.clutterText.connect('key-press-event', (clutterText, event) => { @@ -853,53 +864,25 @@ var DrawingArea = new Lang.Class({ } else if (event.has_shift_modifier() && (event.get_key_symbol() == Clutter.KEY_Return || event.get_key_symbol() == Clutter.KEY_KP_Enter)) { - let startNewLine = true; - this._stopWriting(startNewLine); - clutterText.text = ""; + clutterText.insert_unichar('\n'); return Clutter.EVENT_STOP; } - // 'cursor-changed' signal is not emitted if the text entry is not visible. - // So key events related to the cursor must be listened. - if (event.get_key_symbol() == Clutter.KEY_Left || event.get_key_symbol() == Clutter.KEY_Right || - event.get_key_symbol() == Clutter.KEY_Home || event.get_key_symbol() == Clutter.KEY_End) { - GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - this.currentElement.cursorPosition = clutterText.cursorPosition; - this._updateTextCursorTimeout(); - this.textHasCursor = true; - this._redisplay(); - }); - } - return Clutter.EVENT_PROPAGATE; }); }, - _stopWriting: function(startNewLine) { + _stopWriting: function() { if (this.currentElement.text.length > 0) this.elements.push(this.currentElement); - if (startNewLine && this.currentElement.points.length == 2) { - this.currentElement.lineIndex = this.currentElement.lineIndex || 0; - // copy object, the original keep existing in this.elements - this.currentElement = Object.create(this.currentElement); - this.currentElement.lineIndex ++; - // define a new 'points' array, the original keep existing in this.elements - this.currentElement.points = [ - [this.currentElement.points[0][0], this.currentElement.points[0][1] + this.currentElement.height], - [this.currentElement.points[1][0], this.currentElement.points[1][1] + this.currentElement.height] - ]; - this.currentElement.text = ""; - this.textEntry.set_y(this.currentElement.y); - } else { - this.currentElement = null; - this._stopTextCursorTimeout(); - this.textEntry.destroy(); - delete this.textEntry; - this.grab_key_focus(); - this.updateActionMode(); - this.updatePointerCursor(); - } + this.currentElement = null; + this._stopTextCursorTimeout(); + this.textEntry.destroy(); + delete this.textEntry; + this.grab_key_focus(); + this.updateActionMode(); + this.updatePointerCursor(); this._redisplay(); }, diff --git a/elements.js b/elements.js index 5890187..1267826 100644 --- a/elements.js +++ b/elements.js @@ -645,7 +645,6 @@ const _DrawingElement = new Lang.Class({ }, // The figure rotation center before transformations (original). - // this.textWidth is computed during Cairo building. _getOriginalCenter: function() { if (!this._originalCenter) { let points = this.points; @@ -710,7 +709,6 @@ const TextElement = new Lang.Class({ eraser: this.eraser, transformations: this.transformations, text: this.text, - lineIndex: this.lineIndex !== undefined ? this.lineIndex : undefined, textRightAligned: this.textRightAligned, font: this.font.to_string(), points: this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100]) @@ -730,42 +728,50 @@ const TextElement = new Lang.Class({ return Math.abs(this.points[1][1] - this.points[0][1]); }, - // When rotating grouped lines, lineOffset is used to retrieve the rotation center of the first line. - get lineOffset() { - return (this.lineIndex || 0) * this.height; + // this.lineWidths is computed during Cairo building. + _getLineX: function(index) { + return this.points[1][0] - (this.textRightAligned && this.lineWidths && this.lineWidths[index] ? this.lineWidths[index] : 0); }, _drawCairo: function(cr, params) { - if (this.points.length == 2) { - let layout = PangoCairo.create_layout(cr); - let fontSize = this.height * Pango.SCALE; - this.font.set_absolute_size(fontSize); - 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.set_text(this.text, -1); - PangoCairo.show_layout_line(cr, layout.get_line(0)); + if (this.points.length != 2) + return; + + let layout = PangoCairo.create_layout(cr); + let fontSize = this.height * Pango.SCALE; + this.font.set_absolute_size(fontSize); + layout.set_font_description(this.font); + layout.set_text(this.text, -1); + this.textWidth = layout.get_pixel_size()[0]; + this.lineWidths = layout.get_lines_readonly().map(layoutLine => layoutLine.get_pixel_extents()[1].width) + + layout.get_lines_readonly().forEach((layoutLine, index) => { + cr.moveTo(this._getLineX(index), this.y + this.height * index); + PangoCairo.show_layout_line(cr, layoutLine); + }); + + // Cannot use 'layout.index_to_line_x(cursorPosition, 0)' because character position != byte index + if (params.showTextCursor) { + let layoutCopy = layout.copy(); + if (this.cursorPosition != -1) + layoutCopy.set_text(this.text.slice(0, this.cursorPosition), -1); - if (params.showTextCursor) { - let cursorPosition = this.cursorPosition == -1 ? this.text.length : this.cursorPosition; - layout.set_text(this.text.slice(0, cursorPosition), -1); - let width = layout.get_pixel_size()[0]; - cr.rectangle(this.x + width, this.y, - this.height / 25, - this.height); - cr.fill(); - } - - if (params.showTextRectangle) { - cr.rectangle(this.x, this.y - this.lineOffset, - this.textWidth, - this.height); - setDummyStroke(cr); - } else if (params.drawTextRectangle) { - cr.rectangle(this.x, this.y, - this.textWidth, - this.height); - // Only draw the rectangle to find the element, not to show it. - cr.setLineWidth(0); - } + let cursorLineIndex = layoutCopy.get_line_count() - 1; + let cursorX = this._getLineX(cursorLineIndex) + layoutCopy.get_line_readonly(cursorLineIndex).get_pixel_extents()[1].width; + let cursorY = this.y + this.height * cursorLineIndex; + cr.rectangle(cursorX, cursorY, this.height / 25, - this.height); + cr.fill(); + } + + if (params.showTextRectangle) { + cr.rectangle(this.x, this.y - this.height, + this.textWidth, this.height * layout.get_line_count()); + setDummyStroke(cr); + } else if (params.drawTextRectangle) { + cr.rectangle(this.x, this.y - this.height, + this.textWidth, this.height * layout.get_line_count()); + // Only draw the rectangle to find the element, not to show it. + cr.setLineWidth(0); } }, @@ -774,32 +780,48 @@ const TextElement = new Lang.Class({ }, _drawSvg: function(transAttribute, bgcolorString) { - let row = "\n "; - let [x, y, height] = [Math.round(this.x*100)/100, Math.round(this.y*100)/100, Math.round(this.height*100)/100]; + if (this.points.length != 2) + return ""; + + let row = ""; + let height = Math.round(this.height * 100) / 100; let color = this.eraser ? bgcolorString : this.color.toJSON(); let attributes = this.eraser ? `class="eraser" ` : ''; + attributes += `fill="${color}" ` + + `font-size="${height}" ` + + `font-family="${this.font.get_family()}"`; - if (this.points.length == 2) { - attributes += `fill="${color}" ` + - `font-size="${height}" ` + - `font-family="${this.font.get_family()}"`; - - // this.font.to_string() is not valid to fill the svg 'font' shorthand property. - // Each property must be filled separately. - ['Stretch', 'Style', 'Variant'].forEach(attribute => { - let lower = attribute.toLowerCase(); - if (this.font[`get_${lower}`]() != Pango[attribute].NORMAL) { - let font = new Pango.FontDescription(); - font[`set_${lower}`](this.font[`get_${lower}`]()); - attributes += ` font-${lower}="${font.to_string()}"`; - } - }); - if (this.font.get_weight() != Pango.Weight.NORMAL) - attributes += ` font-weight="${this.font.get_weight()}"`; - row += `${this.text}`; + // this.font.to_string() is not valid to fill the svg 'font' shorthand property. + // Each property must be filled separately. + ['Stretch', 'Style', 'Variant'].forEach(attribute => { + let lower = attribute.toLowerCase(); + if (this.font[`get_${lower}`]() != Pango[attribute].NORMAL) { + let font = new Pango.FontDescription(); + font[`set_${lower}`](this.font[`get_${lower}`]()); + attributes += ` font-${lower}="${font.to_string()}"`; + } + }); + if (this.font.get_weight() != Pango.Weight.NORMAL) + attributes += ` font-weight="${this.font.get_weight()}"`; + + // It is a fallback for thumbnails. The following layout is not the same than the Cairo one and + // layoutLine.get_pixel_extents does not return the correct value with non-latin fonts. + // An alternative would be to store this.lineWidths in the json. + if (this.textRightAligned && !this.lineWidths) { + let clutterText = new Clutter.Text({ text: this.text }); + let layout = clutterText.get_layout(); + let fontSize = height * Pango.SCALE; + this.font.set_absolute_size(fontSize); + layout.set_font_description(this.font); + this.lineWidths = layout.get_lines_readonly().map(layoutLine => layoutLine.get_pixel_extents()[1].width); } + this.text.split(/\r\n|\r|\n/).forEach((text, index) => { + let x = Math.round(this._getLineX(index) * 100) / 100; + let y = Math.round((this.y + this.height * index) * 100) / 100; + row += `\n ${text}`; + }); + return row; }, @@ -827,7 +849,7 @@ const TextElement = new Lang.Class({ _getOriginalCenter: function() { if (!this._originalCenter) { let points = this.points; - this._originalCenter = this.textWidth ? [points[1][0], Math.max(points[0][1], points[1][1]) - this.lineOffset] : + this._originalCenter = points.length == 2 ? [points[1][0], Math.max(points[0][1], points[1][1])] : points.length >= 3 ? getCentroid(points) : getNaiveCenter(points); } From 3223a6fdcaea0ce6c818118b3639e0388f67a6ef Mon Sep 17 00:00:00 2001 From: abakkk Date: Wed, 17 Feb 2021 12:00:23 +0100 Subject: [PATCH 03/12] return to start a new text line `Shift + Return` was not compatible with the ibusCandidatePopup. So the only way to "validate" a text element is to click outside the text area. Related to #56. --- area.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/area.js b/area.js index d447ae7..b1975e8 100644 --- a/area.js +++ b/area.js @@ -808,7 +808,7 @@ var DrawingArea = new Lang.Class({ this.currentElement.cursorPosition = 0; // Translators: %s is a key label this.emit('show-osd', Files.Icons.TOOL_TEXT, _("Press %s\nto start a new line") - .format(Gtk.accelerator_get_label(Clutter.KEY_Return, 1)), "", -1, true); + .format(Gtk.accelerator_get_label(Clutter.KEY_Return, 0)), "", -1, true); this._updateTextCursorTimeout(); this.textHasCursor = true; this._redisplay(); @@ -835,6 +835,7 @@ var DrawingArea = new Lang.Class({ } this.textEntry.clutterText.set_single_line_mode(false); + this.textEntry.clutterText.set_activatable(false); this.textEntry.clutterText.connect('activate', (clutterText) => { this._stopWriting(); }); @@ -861,11 +862,6 @@ var DrawingArea = new Lang.Class({ this.currentElement.text = ""; this._stopWriting(); return Clutter.EVENT_STOP; - } else if (event.has_shift_modifier() && - (event.get_key_symbol() == Clutter.KEY_Return || - event.get_key_symbol() == Clutter.KEY_KP_Enter)) { - clutterText.insert_unichar('\n'); - return Clutter.EVENT_STOP; } return Clutter.EVENT_PROPAGATE; From cb49c83799829e102203377b24b3eda16951c090 Mon Sep 17 00:00:00 2001 From: abakkk Date: Wed, 17 Feb 2021 14:27:35 +0100 Subject: [PATCH 04/12] add centered text alignment --- area.js | 16 +++++----- elements.js | 22 ++++++++++--- files.js | 2 +- locale/draw-on-your-screen.pot | 16 ++++++---- menu.js | 32 +++++++++++++++++-- ...extensions.draw-on-your-screen.gschema.xml | 2 +- 6 files changed, 67 insertions(+), 23 deletions(-) diff --git a/area.js b/area.js index b1975e8..57c2b68 100644 --- a/area.js +++ b/area.js @@ -53,7 +53,7 @@ const GRID_TILES_HORIZONTAL_NUMBER = 30; const COLOR_PICKER_EXTENSION_UUID = 'color-picker@tuberry'; const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_'); -const { Shapes, Transformations } = Elements; +const { Shapes, TextAlignment, Transformations } = Elements; const { DisplayStrings } = Menu; const FontGenericFamilies = ['Sans-Serif', 'Serif', 'Monospace', 'Cursive', 'Fantasy']; @@ -152,7 +152,7 @@ var DrawingArea = new Lang.Class({ this.currentElement = null; this.currentTool = Shapes.NONE; this.currentImage = null; - this.currentTextRightAligned = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; + this.currentTextAlignment = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL ? TextAlignment.RIGHT : TextAlignment.LEFT; let fontName = St.Settings && St.Settings.get().font_name || Convenience.getSettings('org.gnome.desktop.interface').get_string('font-name'); this.currentFont = Pango.FontDescription.from_string(fontName); this.currentFont.unset_fields(Pango.FontMask.SIZE); @@ -677,7 +677,7 @@ var DrawingArea = new Lang.Class({ font: this.currentFont.copy(), // Translators: initial content of the text area text: pgettext("text-area-content", "Text"), - textRightAligned: this.currentTextRightAligned, + textAlignment: this.currentTextAlignment, points: [] }); } else if (this.currentTool == Shapes.IMAGE) { @@ -1151,13 +1151,13 @@ var DrawingArea = new Lang.Class({ }, switchTextAlignment: function() { - this.currentTextRightAligned = !this.currentTextRightAligned; - if (this.currentElement && this.currentElement.textRightAligned !== undefined) { - this.currentElement.textRightAligned = this.currentTextRightAligned; + this.currentTextAlignment = this.currentTextAlignment == 2 ? 0 : this.currentTextAlignment + 1; + if (this.currentElement && this.currentElement.textAlignment != this.currentTextAlignment) { + this.currentElement.textAlignment = this.currentTextAlignment; this._redisplay(); } - let icon = Files.Icons[this.currentTextRightAligned ? 'RIGHT_ALIGNED' : 'LEFT_ALIGNED']; - this.emit('show-osd', icon, DisplayStrings.getTextAlignment(this.currentTextRightAligned), "", -1, false); + let icon = Files.Icons[this.currentTextAlignment == TextAlignment.RIGHT ? 'RIGHT_ALIGNED' : this.currentTextAlignment == TextAlignment.CENTER ? 'CENTERED' : 'LEFT_ALIGNED']; + this.emit('show-osd', icon, DisplayStrings.TextAlignment[this.currentTextAlignment], "", -1, false); }, switchImageFile: function(reverse) { diff --git a/elements.js b/elements.js index 1267826..4ed73e5 100644 --- a/elements.js +++ b/elements.js @@ -31,6 +31,7 @@ const Me = imports.misc.extensionUtils.getCurrentExtension(); const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_'); var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6, IMAGE: 7 }; +var TextAlignment = { LEFT: 0, CENTER: 1, RIGHT: 2 }; var Transformations = { TRANSLATION: 0, ROTATION: 1, SCALE_PRESERVE: 2, STRETCH: 3, REFLECTION: 4, INVERSION: 5, SMOOTH: 100 }; var getAllFontFamilies = function() { @@ -111,6 +112,11 @@ const _DrawingElement = new Lang.Class({ this.points.push([ratio * (p1[0] - p0[0]) + p0[0], ratio * (p1[1] - p0[1]) + p0[1]]); } delete this.transform; + + // v10- + if (this.textRightAligned) + this.textAlignment = TextAlignment.RIGHT; + delete this.textRightAligned; }, // toJSON is called by JSON.stringify @@ -709,7 +715,7 @@ const TextElement = new Lang.Class({ eraser: this.eraser, transformations: this.transformations, text: this.text, - textRightAligned: this.textRightAligned, + textAlignment: this.textAlignment, font: this.font.to_string(), points: this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100]) }; @@ -717,7 +723,11 @@ const TextElement = new Lang.Class({ get x() { // this.textWidth is computed during Cairo building. - return this.points[1][0] - (this.textRightAligned ? this.textWidth : 0); + let offset = this.textAlignment == TextAlignment.RIGHT ? this.textWidth : + this.textAlignment == TextAlignment.CENTER ? this.textWidth / 2 : + 0; + + return this.points[1][0] - offset; }, get y() { @@ -730,7 +740,11 @@ const TextElement = new Lang.Class({ // this.lineWidths is computed during Cairo building. _getLineX: function(index) { - return this.points[1][0] - (this.textRightAligned && this.lineWidths && this.lineWidths[index] ? this.lineWidths[index] : 0); + let offset = this.textAlignment == TextAlignment.RIGHT && this.lineWidths && this.lineWidths[index] ? this.lineWidths[index] : + this.textAlignment == TextAlignment.CENTER && this.lineWidths && this.lineWidths[index] ? this.lineWidths[index] / 2 : + 0; + + return this.points[1][0] - offset; }, _drawCairo: function(cr, params) { @@ -807,7 +821,7 @@ const TextElement = new Lang.Class({ // It is a fallback for thumbnails. The following layout is not the same than the Cairo one and // layoutLine.get_pixel_extents does not return the correct value with non-latin fonts. // An alternative would be to store this.lineWidths in the json. - if (this.textRightAligned && !this.lineWidths) { + if (this.textAlignment != TextAlignment.LEFT && !this.lineWidths) { let clutterText = new Clutter.Text({ text: this.text }); let layout = clutterText.get_layout(); let fontSize = height * Pango.SCALE; diff --git a/files.js b/files.js index 1f31691..d7fb60f 100644 --- a/files.js +++ b/files.js @@ -47,7 +47,7 @@ const ThemedIconNames = { GRAB: 'input-touchpad', UNGRAB: 'touchpad-disabled', OPEN: 'document-open', SAVE: 'document-save', FONT_FAMILY: 'font-x-generic', FONT_STYLE: 'format-text-italic', FONT_WEIGHT:'format-text-bold', - LEFT_ALIGNED: 'format-justify-left', RIGHT_ALIGNED: 'format-justify-right', + LEFT_ALIGNED: 'format-justify-left', CENTERED: 'format-justify-center',RIGHT_ALIGNED: 'format-justify-right', TOOL_IMAGE: 'insert-image', TOOL_TEXT: 'insert-text', }; diff --git a/locale/draw-on-your-screen.pot b/locale/draw-on-your-screen.pot index 5de5eaf..a36dc63 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-10-04 22:45+0200\n" +"POT-Creation-Date: 2021-02-17 14:14+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -44,7 +44,9 @@ msgstr "" #. Translators: %s is a key label #, javascript-format -msgid "Press %s\nto start a new line" +msgid "" +"Press %s\n" +"to start a new line" msgstr "" #. Translators: It is displayed in an OSD notification to ask the user to start picking, so it should use the imperative mood. @@ -214,11 +216,13 @@ msgstr "" msgid "%f px" msgstr "" -#. Translators: text alignment -msgid "Right aligned" +msgid "Left aligned" msgstr "" -msgid "Left aligned" +msgid "Centered" +msgstr "" + +msgid "Right aligned" msgstr "" msgctxt "drawing-tool" @@ -685,7 +689,7 @@ msgstr "" msgid "Change linejoin" msgstr "" -msgid "Toggle text alignment" +msgid "Change text alignment" msgstr "" msgid "Add a drawing background" diff --git a/menu.js b/menu.js index 909d668..6d58268 100644 --- a/menu.js +++ b/menu.js @@ -47,6 +47,7 @@ const FONT_FAMILY_STYLE = true; // use 'login-dialog-message-warning' class in order to get GS theme warning color (default: #f57900) const WARNING_COLOR_STYLE_CLASS_NAME = 'login-dialog-message-warning'; const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_'); +const TextAlignmentIcon = { 0: Files.Icons.LEFT_ALIGNED, 1: Files.Icons.CENTERED, 2: Files.Icons.RIGHT_ALIGNED }; const getActor = function(object) { return GS_VERSION < '3.33.0' ? object.actor : object; @@ -124,9 +125,12 @@ var DisplayStrings = { return _("%f px").format(value); }, - getTextAlignment: function(rightAligned) { + get TextAlignment() { // Translators: text alignment - return rightAligned ? _("Right aligned") : _("Left aligned"); + if (!this._textAlignments) + this._textAlignments = { 0: _("Left aligned"), 1: _("Centered"), 2: _("Right aligned") }; + + return this._textAlignments; }, get Tool() { @@ -268,7 +272,7 @@ var DrawingMenu = new Lang.Class({ this._addFontFamilySubMenuItem(fontSection, Files.Icons.FONT_FAMILY); this._addSubMenuItem(fontSection, Files.Icons.FONT_WEIGHT, DisplayStrings.FontWeight, this.area, 'currentFontWeight'); this._addSubMenuItem(fontSection, Files.Icons.FONT_STYLE, DisplayStrings.FontStyle, this.area, 'currentFontStyle'); - this._addSwitchItem(fontSection, DisplayStrings.getTextAlignment(true), Files.Icons.LEFT_ALIGNED, Files.Icons.RIGHT_ALIGNED, this.area, 'currentTextRightAligned'); + this._addTextAlignmentSubMenuItem(fontSection); this._addSeparator(fontSection); this.menu.addMenuItem(fontSection); fontSection.itemActivated = () => {}; @@ -553,6 +557,28 @@ var DrawingMenu = new Lang.Class({ menu.addMenuItem(item); }, + _addTextAlignmentSubMenuItem: function(menu) { + let item = new PopupMenu.PopupSubMenuMenuItem(DisplayStrings.TextAlignment[this.area.currentTextAlignment], true); + item.icon.set_gicon(TextAlignmentIcon[this.area.currentTextAlignment]); + + item.menu.itemActivated = item.menu.close; + + GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { + Object.keys(TextAlignmentIcon).forEach(key => { + let subItem = item.menu.addAction(DisplayStrings.TextAlignment[key], () => { + item.label.set_text(DisplayStrings.TextAlignment[key]); + this.area.currentTextAlignment = key; + item.icon.set_gicon(TextAlignmentIcon[key]); + }); + + getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment); + }); + return GLib.SOURCE_REMOVE; + }); + + menu.addMenuItem(item); + }, + _addImageSubMenuItem: function(menu, images) { let item = new PopupMenu.PopupSubMenuMenuItem('', true); item.update = () => { 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 1b26430..456b144 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 @@ -315,7 +315,7 @@ ["<Primary><Alt>a"] - Toggle text alignment + Change text alignment ["<Primary>b"] From ca6baf0dc89e85bcd4128b928bc60bb352ca28bc Mon Sep 17 00:00:00 2001 From: abakkk Date: Wed, 17 Feb 2021 14:53:13 +0100 Subject: [PATCH 05/12] minor, enumeration syntax --- area.js | 102 ++++++++++++++++++------------------ elements.js | 144 +++++++++++++++++++++++++-------------------------- extension.js | 22 ++++---- files.js | 6 +-- menu.js | 88 +++++++++++++++---------------- 5 files changed, 181 insertions(+), 181 deletions(-) diff --git a/area.js b/area.js index 57c2b68..ea0eebd 100644 --- a/area.js +++ b/area.js @@ -1,5 +1,5 @@ /* jslint esversion: 6 */ -/* exported Tools, DrawingArea */ +/* exported Tool, DrawingArea */ /* * Copyright 2019 Abakkk @@ -53,17 +53,17 @@ const GRID_TILES_HORIZONTAL_NUMBER = 30; const COLOR_PICKER_EXTENSION_UUID = 'color-picker@tuberry'; const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_'); -const { Shapes, TextAlignment, Transformations } = Elements; +const { Shape, TextAlignment, Transformation } = Elements; const { DisplayStrings } = Menu; const FontGenericFamilies = ['Sans-Serif', 'Serif', 'Monospace', 'Cursive', 'Fantasy']; -const Manipulations = { MOVE: 100, RESIZE: 101, MIRROR: 102 }; -var Tools = Object.assign({ +const Manipulation = { MOVE: 100, RESIZE: 101, MIRROR: 102 }; +var Tool = Object.assign({ getNameOf: function(value) { return Object.keys(this).find(key => this[key] == value); } -}, Shapes, Manipulations); -Object.defineProperty(Tools, 'getNameOf', { enumerable: false }); +}, Shape, Manipulation); +Object.defineProperty(Tool, 'getNameOf', { enumerable: false }); // toJSON provides a string suitable for SVG color attribute whereas // toString provides a string suitable for displaying the color name to the user. @@ -150,7 +150,7 @@ var DrawingArea = new Lang.Class({ this.elements = []; this.undoneElements = []; this.currentElement = null; - this.currentTool = Shapes.NONE; + this.currentTool = Shape.NONE; this.currentImage = null; this.currentTextAlignment = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL ? TextAlignment.RIGHT : TextAlignment.LEFT; let fontName = St.Settings && St.Settings.get().font_name || Convenience.getSettings('org.gnome.desktop.interface').get_string('font-name'); @@ -178,7 +178,7 @@ var DrawingArea = new Lang.Class({ get menu() { if (!this._menu) - this._menu = new Menu.DrawingMenu(this, this.monitor, Tools, this.areaManagerUtils); + this._menu = new Menu.DrawingMenu(this, this.monitor, Tool, this.areaManagerUtils); return this._menu; }, @@ -251,7 +251,7 @@ var DrawingArea = new Lang.Class({ get hasManipulationTool() { // No Object.values method in GS 3.24. - return Object.keys(Manipulations).map(key => Manipulations[key]).indexOf(this.currentTool) != -1; + return Object.keys(Manipulation).map(key => Manipulation[key]).indexOf(this.currentTool) != -1; }, // Boolean wrapper for switch menu item. @@ -325,7 +325,7 @@ var DrawingArea = new Lang.Class({ if (this.elements[i].fill && !this.elements[i].isStraightLine) { cr.fillPreserve(); - if (this.elements[i].shape == Shapes.NONE || this.elements[i].shape == Shapes.LINE) + if (this.elements[i].shape == Shape.NONE || this.elements[i].shape == Shape.LINE) cr.closePath(); } @@ -335,7 +335,7 @@ var DrawingArea = new Lang.Class({ if (this.currentElement && this.currentElement.eraser) { this.currentElement.buildCairo(cr, { showTextCursor: this.textHasCursor, - showTextRectangle: this.currentElement.shape != Shapes.TEXT || !this.isWriting, + showTextRectangle: this.currentElement.shape != Shape.TEXT || !this.isWriting, dummyStroke: this.currentElement.fill && this.currentElement.line.lineWidth == 0 }); cr.stroke(); } @@ -346,7 +346,7 @@ var DrawingArea = new Lang.Class({ return; this.currentElement.buildCairo(cr, { showTextCursor: this.textHasCursor, - showTextRectangle: this.currentElement.shape != Shapes.TEXT || !this.isWriting, + showTextRectangle: this.currentElement.shape != Shape.TEXT || !this.isWriting, dummyStroke: this.currentElement.fill && this.currentElement.line.lineWidth == 0 }); cr.stroke(); }, @@ -379,11 +379,11 @@ var DrawingArea = new Lang.Class({ }, _getHasImageBack: function() { - return this.elements.some(element => element.shape == Shapes.IMAGE); + return this.elements.some(element => element.shape == Shape.IMAGE); }, _getHasImageFore: function() { - return this.currentElement && this.currentElement.shape == Shapes.IMAGE || false; + return this.currentElement && this.currentElement.shape == Shape.IMAGE || false; }, _redisplay: function() { @@ -411,7 +411,7 @@ var DrawingArea = new Lang.Class({ let controlPressed = event.has_control_modifier(); let shiftPressed = event.has_shift_modifier(); - if (this.currentElement && this.currentElement.shape == Shapes.TEXT && this.isWriting) + if (this.currentElement && this.currentElement.shape == Shape.TEXT && this.isWriting) // finish writing this._stopWriting(); @@ -471,7 +471,7 @@ var DrawingArea = new Lang.Class({ }, _onKeyPressed: function(actor, event) { - if (this.currentElement && this.currentElement.shape == Shapes.LINE && + if (this.currentElement && this.currentElement.shape == Shape.LINE && (event.get_key_symbol() == Clutter.KEY_Return || event.get_key_symbol() == Clutter.KEY_KP_Enter || event.get_key_symbol() == Clutter.KEY_Control_L)) { @@ -485,7 +485,7 @@ var DrawingArea = new Lang.Class({ this._redisplay(); return Clutter.EVENT_STOP; } else if (this.currentElement && - (this.currentElement.shape == Shapes.POLYGON || this.currentElement.shape == Shapes.POLYLINE) && + (this.currentElement.shape == Shape.POLYGON || this.currentElement.shape == Shape.POLYLINE) && (event.get_key_symbol() == Clutter.KEY_Return || event.get_key_symbol() == Clutter.KEY_KP_Enter)) { this.currentElement.addPoint(); @@ -560,7 +560,7 @@ var DrawingArea = new Lang.Class({ if (!success) return; - if (this.currentTool == Manipulations.MIRROR) { + if (this.currentTool == Manipulation.MIRROR) { this.grabbedElementLocked = !this.grabbedElementLocked; if (this.grabbedElementLocked) { this.updatePointerCursor(); @@ -591,12 +591,12 @@ var DrawingArea = new Lang.Class({ let undoable = !duplicate; - if (this.currentTool == Manipulations.MOVE) - this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformations.ROTATION : Transformations.TRANSLATION, undoable); - else if (this.currentTool == Manipulations.RESIZE) - this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformations.STRETCH : Transformations.SCALE_PRESERVE, undoable); - else if (this.currentTool == Manipulations.MIRROR) { - this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformations.INVERSION : Transformations.REFLECTION, undoable); + if (this.currentTool == Manipulation.MOVE) + this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformation.ROTATION : Transformation.TRANSLATION, undoable); + else if (this.currentTool == Manipulation.RESIZE) + this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformation.STRETCH : Transformation.SCALE_PRESERVE, undoable); + else if (this.currentTool == Manipulation.MIRROR) { + this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformation.INVERSION : Transformation.REFLECTION, undoable); this._redisplay(); } @@ -616,28 +616,28 @@ var DrawingArea = new Lang.Class({ _updateTransforming: function(x, y, controlPressed) { let undoable = this.grabbedElement.lastTransformation.undoable || false; - if (controlPressed && this.grabbedElement.lastTransformation.type == Transformations.TRANSLATION) { + if (controlPressed && this.grabbedElement.lastTransformation.type == Transformation.TRANSLATION) { this.grabbedElement.stopTransformation(); - this.grabbedElement.startTransformation(x, y, Transformations.ROTATION, undoable); - } else if (!controlPressed && this.grabbedElement.lastTransformation.type == Transformations.ROTATION) { + this.grabbedElement.startTransformation(x, y, Transformation.ROTATION, undoable); + } else if (!controlPressed && this.grabbedElement.lastTransformation.type == Transformation.ROTATION) { this.grabbedElement.stopTransformation(); - this.grabbedElement.startTransformation(x, y, Transformations.TRANSLATION, undoable); + this.grabbedElement.startTransformation(x, y, Transformation.TRANSLATION, undoable); } - if (controlPressed && this.grabbedElement.lastTransformation.type == Transformations.SCALE_PRESERVE) { + if (controlPressed && this.grabbedElement.lastTransformation.type == Transformation.SCALE_PRESERVE) { this.grabbedElement.stopTransformation(); - this.grabbedElement.startTransformation(x, y, Transformations.STRETCH, undoable); - } else if (!controlPressed && this.grabbedElement.lastTransformation.type == Transformations.STRETCH) { + this.grabbedElement.startTransformation(x, y, Transformation.STRETCH, undoable); + } else if (!controlPressed && this.grabbedElement.lastTransformation.type == Transformation.STRETCH) { this.grabbedElement.stopTransformation(); - this.grabbedElement.startTransformation(x, y, Transformations.SCALE_PRESERVE, undoable); + this.grabbedElement.startTransformation(x, y, Transformation.SCALE_PRESERVE, undoable); } - if (controlPressed && this.grabbedElement.lastTransformation.type == Transformations.REFLECTION) { + if (controlPressed && this.grabbedElement.lastTransformation.type == Transformation.REFLECTION) { this.grabbedElement.transformations.pop(); - this.grabbedElement.startTransformation(x, y, Transformations.INVERSION, undoable); - } else if (!controlPressed && this.grabbedElement.lastTransformation.type == Transformations.INVERSION) { + this.grabbedElement.startTransformation(x, y, Transformation.INVERSION, undoable); + } else if (!controlPressed && this.grabbedElement.lastTransformation.type == Transformation.INVERSION) { this.grabbedElement.transformations.pop(); - this.grabbedElement.startTransformation(x, y, Transformations.REFLECTION, undoable); + this.grabbedElement.startTransformation(x, y, Transformation.REFLECTION, undoable); } this.grabbedElement.updateTransformation(x, y); @@ -669,7 +669,7 @@ var DrawingArea = new Lang.Class({ this._stopDrawing(); }); - if (this.currentTool == Shapes.TEXT) { + if (this.currentTool == Shape.TEXT) { this.currentElement = new Elements.DrawingElement({ shape: this.currentTool, color: this.currentColor, @@ -680,7 +680,7 @@ var DrawingArea = new Lang.Class({ textAlignment: this.currentTextAlignment, points: [] }); - } else if (this.currentTool == Shapes.IMAGE) { + } else if (this.currentTool == Shape.IMAGE) { this.currentElement = new Elements.DrawingElement({ shape: this.currentTool, color: this.currentColor, @@ -703,8 +703,8 @@ var DrawingArea = new Lang.Class({ this.currentElement.startDrawing(startX, startY); - if (this.currentTool == Shapes.POLYGON || this.currentTool == Shapes.POLYLINE) { - let icon = Files.Icons[this.currentTool == Shapes.POLYGON ? 'TOOL_POLYGON' : 'TOOL_POLYLINE']; + if (this.currentTool == Shape.POLYGON || this.currentTool == Shape.POLYLINE) { + let icon = Files.Icons[this.currentTool == Shape.POLYGON ? 'TOOL_POLYGON' : 'TOOL_POLYLINE']; // Translators: %s is a key label this.emit('show-osd', icon, _("Press %s to mark vertices") .format(Gtk.accelerator_get_label(Clutter.KEY_Return, 0)), "", -1, true); @@ -727,7 +727,7 @@ var DrawingArea = new Lang.Class({ let controlPressed = event.has_control_modifier(); this._updateDrawing(x, y, controlPressed); - if (this.currentTool == Shapes.NONE) { + if (this.currentTool == Shape.NONE) { let device = event.get_device(); let sequence = event.get_event_sequence(); @@ -781,14 +781,14 @@ var DrawingArea = new Lang.Class({ } // skip when a polygon has not at least 3 points - if (this.currentElement && this.currentElement.shape == Shapes.POLYGON && this.currentElement.points.length < 3) + if (this.currentElement && this.currentElement.shape == Shape.POLYGON && this.currentElement.points.length < 3) this.currentElement = null; if (this.currentElement) this.currentElement.stopDrawing(); if (this.currentElement && this.currentElement.points.length >= 2) { - if (this.currentElement.shape == Shapes.TEXT && !this.isWriting) { + if (this.currentElement.shape == Shape.TEXT && !this.isWriting) { this._startWriting(); return; } @@ -891,15 +891,15 @@ var DrawingArea = new Lang.Class({ }, updatePointerCursor: function(controlPressed) { - if (this.currentTool == Manipulations.MIRROR && this.grabbedElementLocked) + if (this.currentTool == Manipulation.MIRROR && this.grabbedElementLocked) this.setPointerCursor('CROSSHAIR'); else if (this.hasManipulationTool) this.setPointerCursor(this.grabbedElement ? 'MOVE_OR_RESIZE_WINDOW' : 'DEFAULT'); - else if (this.currentElement && this.currentElement.shape == Shapes.TEXT && this.isWriting) + else if (this.currentElement && this.currentElement.shape == Shape.TEXT && this.isWriting) this.setPointerCursor('IBEAM'); else if (!this.currentElement) - this.setPointerCursor(this.currentTool == Shapes.NONE ? 'POINTING_HAND' : 'CROSSHAIR'); - else if (this.currentElement.shape != Shapes.NONE && controlPressed) + this.setPointerCursor(this.currentTool == Shape.NONE ? 'POINTING_HAND' : 'CROSSHAIR'); + else if (this.currentElement.shape != Shape.NONE && controlPressed) this.setPointerCursor('MOVE_OR_RESIZE_WINDOW'); }, @@ -988,7 +988,7 @@ var DrawingArea = new Lang.Class({ }, smoothLastElement: function() { - if (this.elements.length > 0 && this.elements[this.elements.length - 1].shape == Shapes.NONE) { + if (this.elements.length > 0 && this.elements[this.elements.length - 1].shape == Shape.NONE) { this.elements[this.elements.length - 1].smoothAll(); this._redisplay(); } @@ -1069,7 +1069,7 @@ var DrawingArea = new Lang.Class({ selectTool: function(tool) { this.currentTool = tool; - this.emit('show-osd', Files.Icons[`TOOL_${Tools.getNameOf(tool)}`] || null, DisplayStrings.Tool[tool], "", -1, false); + this.emit('show-osd', Files.Icons[`TOOL_${Tool.getNameOf(tool)}`] || null, DisplayStrings.Tool[tool], "", -1, false); this.updatePointerCursor(); }, @@ -1169,7 +1169,7 @@ var DrawingArea = new Lang.Class({ pasteImageFiles: function() { Files.Images.addImagesFromClipboard(lastImage => { this.currentImage = lastImage; - this.currentTool = Shapes.IMAGE; + this.currentTool = Shape.IMAGE; this.updatePointerCursor(); this.emit('show-osd', this.currentImage.gicon, this.currentImage.toString(), "", -1, false); }); @@ -1369,7 +1369,7 @@ var DrawingArea = new Lang.Class({ this._stopAll(); let prefixes = 'xmlns="http://www.w3.org/2000/svg"'; - if (this.elements.some(element => element.shape == Shapes.IMAGE)) + if (this.elements.some(element => element.shape == Shape.IMAGE)) prefixes += ' xmlns:xlink="http://www.w3.org/1999/xlink"'; let content = ``; let backgroundColorString = this.hasBackground ? String(this.areaBackgroundColor) : 'transparent'; diff --git a/elements.js b/elements.js index 4ed73e5..c1249a8 100644 --- a/elements.js +++ b/elements.js @@ -1,5 +1,5 @@ /* jslint esversion: 6 */ -/* exported Shapes, Transformations, getAllFontFamilies, DrawingElement */ +/* exported Shape, TextAlignment, Transformation, getAllFontFamilies, DrawingElement */ /* * Copyright 2019 Abakkk @@ -30,9 +30,9 @@ const PangoCairo = imports.gi.PangoCairo; const Me = imports.misc.extensionUtils.getCurrentExtension(); const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_'); -var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6, IMAGE: 7 }; +var Shape = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6, IMAGE: 7 }; var TextAlignment = { LEFT: 0, CENTER: 1, RIGHT: 2 }; -var Transformations = { TRANSLATION: 0, ROTATION: 1, SCALE_PRESERVE: 2, STRETCH: 3, REFLECTION: 4, INVERSION: 5, SMOOTH: 100 }; +var Transformation = { TRANSLATION: 0, ROTATION: 1, SCALE_PRESERVE: 2, STRETCH: 3, REFLECTION: 4, INVERSION: 5, SMOOTH: 100 }; var getAllFontFamilies = function() { return PangoCairo.font_map_get_default().list_families().map(fontFamily => fontFamily.get_name()).sort((a,b) => a.localeCompare(b)); @@ -66,8 +66,8 @@ const MIN_DRAWING_SIZE = 3; // px const MIN_INTERMEDIATE_POINT_DISTANCE = 1; // px, the higher it is, the fewer points there will be var DrawingElement = function(params) { - return params.shape == Shapes.TEXT ? new TextElement(params) : - params.shape == Shapes.IMAGE ? new ImageElement(params) : + return params.shape == Shape.TEXT ? new TextElement(params) : + params.shape == Shape.IMAGE ? new ImageElement(params) : new _DrawingElement(params); }; @@ -104,9 +104,9 @@ const _DrawingElement = new Lang.Class({ if (params.transform && params.transform.center) { let angle = (params.transform.angle || 0) + (params.transform.startAngle || 0); if (angle) - this.transformations.push({ type: Transformations.ROTATION, angle: angle }); + this.transformations.push({ type: Transformation.ROTATION, angle: angle }); } - if (params.shape == Shapes.ELLIPSE && params.transform && params.transform.ratio && params.transform.ratio != 1 && params.points.length >= 2) { + if (params.shape == Shape.ELLIPSE && params.transform && params.transform.ratio && params.transform.ratio != 1 && params.points.length >= 2) { let [ratio, p0, p1] = [params.transform.ratio, params.points[0], params.points[1]]; // Add a fake point that will give the right ellipse ratio when building the element. this.points.push([ratio * (p1[0] - p0[0]) + p0[0], ratio * (p1[1] - p0[1]) + p0[1]]); @@ -129,7 +129,7 @@ const _DrawingElement = new Lang.Class({ fill: this.fill, fillRule: this.fillRule, eraser: this.eraser, - transformations: this.transformations.filter(transformation => transformation.type != Transformations.SMOOTH) + transformations: this.transformations.filter(transformation => transformation.type != Transformation.SMOOTH) .map(transformation => Object.assign({}, transformation, { undoable: undefined })), points: this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100]) }; @@ -142,7 +142,7 @@ const _DrawingElement = new Lang.Class({ if (this.showSymmetryElement) { let transformation = this.lastTransformation; setDummyStroke(cr); - if (transformation.type == Transformations.REFLECTION) { + if (transformation.type == Transformation.REFLECTION) { cr.moveTo(transformation.startX, transformation.startY); cr.lineTo(transformation.endX, transformation.endY); } else { @@ -177,21 +177,21 @@ const _DrawingElement = new Lang.Class({ } this.transformations.slice(0).reverse().forEach(transformation => { - if (transformation.type == Transformations.TRANSLATION) { + if (transformation.type == Transformation.TRANSLATION) { cr.translate(transformation.slideX, transformation.slideY); - } else if (transformation.type == Transformations.ROTATION) { + } else if (transformation.type == Transformation.ROTATION) { let center = this._getTransformedCenter(transformation); cr.translate(center[0], center[1]); cr.rotate(transformation.angle); cr.translate(-center[0], -center[1]); - } else if (transformation.type == Transformations.SCALE_PRESERVE || transformation.type == Transformations.STRETCH) { + } else if (transformation.type == Transformation.SCALE_PRESERVE || transformation.type == Transformation.STRETCH) { let center = this._getTransformedCenter(transformation); cr.translate(center[0], center[1]); cr.rotate(transformation.angle); cr.scale(transformation.scaleX, transformation.scaleY); cr.rotate(-transformation.angle); cr.translate(-center[0], -center[1]); - } else if (transformation.type == Transformations.REFLECTION || transformation.type == Transformations.INVERSION) { + } else if (transformation.type == Transformation.REFLECTION || transformation.type == Transformation.INVERSION) { cr.translate(transformation.slideX, transformation.slideY); cr.rotate(transformation.angle); cr.scale(transformation.scaleX, transformation.scaleY); @@ -208,21 +208,21 @@ const _DrawingElement = new Lang.Class({ _drawCairo: function(cr, params) { let [points, shape] = [this.points, this.shape]; - if (shape == Shapes.LINE && points.length == 3) { + if (shape == Shape.LINE && points.length == 3) { cr.moveTo(points[0][0], points[0][1]); cr.curveTo(points[0][0], points[0][1], points[1][0], points[1][1], points[2][0], points[2][1]); - } else if (shape == Shapes.LINE && points.length == 4) { + } else if (shape == Shape.LINE && points.length == 4) { cr.moveTo(points[0][0], points[0][1]); cr.curveTo(points[1][0], points[1][1], points[2][0], points[2][1], points[3][0], points[3][1]); - } else if (shape == Shapes.NONE || shape == Shapes.LINE) { + } else if (shape == Shape.NONE || shape == Shape.LINE) { cr.moveTo(points[0][0], points[0][1]); for (let j = 1; j < points.length; j++) { cr.lineTo(points[j][0], points[j][1]); } - } else if (shape == Shapes.ELLIPSE && points.length >= 2) { + } else if (shape == Shape.ELLIPSE && points.length >= 2) { let radius = Math.hypot(points[1][0] - points[0][0], points[1][1] - points[0][1]); let ratio = 1; @@ -238,15 +238,15 @@ const _DrawingElement = new Lang.Class({ } else cr.arc(points[0][0], points[0][1], radius, 0, 2 * Math.PI); - } else if (shape == Shapes.RECTANGLE && points.length == 2) { + } else if (shape == Shape.RECTANGLE && points.length == 2) { cr.rectangle(points[0][0], points[0][1], points[1][0] - points[0][0], points[1][1] - points[0][1]); - } else if ((shape == Shapes.POLYGON || shape == Shapes.POLYLINE) && points.length >= 2) { + } else if ((shape == Shape.POLYGON || shape == Shape.POLYLINE) && points.length >= 2) { cr.moveTo(points[0][0], points[0][1]); for (let j = 1; j < points.length; j++) { cr.lineTo(points[j][0], points[j][1]); } - if (shape == Shapes.POLYGON) + if (shape == Shape.POLYGON) cr.closePath(); } @@ -268,19 +268,19 @@ const _DrawingElement = new Lang.Class({ this.transformations.slice(0).reverse().forEach(transformation => { let center = this._getTransformedCenter(transformation); - if (transformation.type == Transformations.TRANSLATION) { + if (transformation.type == Transformation.TRANSLATION) { transforms.push(['translate', transformation.slideX, transformation.slideY]); - } else if (transformation.type == Transformations.ROTATION) { + } else if (transformation.type == Transformation.ROTATION) { transforms.push(['translate', center[0], center[1]]); transforms.push(['rotate', transformation.angle * RADIAN]); transforms.push(['translate', -center[0], -center[1]]); - } else if (transformation.type == Transformations.SCALE_PRESERVE || transformation.type == Transformations.STRETCH) { + } else if (transformation.type == Transformation.SCALE_PRESERVE || transformation.type == Transformation.STRETCH) { transforms.push(['translate', center[0], center[1]]); transforms.push(['rotate', transformation.angle * RADIAN]); transforms.push(['scale', transformation.scaleX, transformation.scaleY]); transforms.push(['rotate', -transformation.angle * RADIAN]); transforms.push(['translate', -center[0], -center[1]]); - } else if (transformation.type == Transformations.REFLECTION || transformation.type == Transformations.INVERSION) { + } else if (transformation.type == Transformation.REFLECTION || transformation.type == Transformation.INVERSION) { transforms.push(['translate', transformation.slideX, transformation.slideY]); transforms.push(['rotate', transformation.angle * RADIAN]); transforms.push(['scale', transformation.scaleX, transformation.scaleY]); @@ -348,45 +348,45 @@ const _DrawingElement = new Lang.Class({ attributes += ` stroke-dasharray="${this.dash.array[0]} ${this.dash.array[1]}" stroke-dashoffset="${this.dash.offset}"`; } - if (this.shape == Shapes.LINE && points.length == 4) { + if (this.shape == Shape.LINE && points.length == 4) { row += ``; - } else if (this.shape == Shapes.LINE && points.length == 3) { + } else if (this.shape == Shape.LINE && points.length == 3) { row += ``; - } else if (this.shape == Shapes.LINE) { + } else if (this.shape == Shape.LINE) { row += ``; - } else if (this.shape == Shapes.NONE) { + } else if (this.shape == Shape.NONE) { row += ``; - } else if (this.shape == Shapes.ELLIPSE && points.length == 3) { + } else if (this.shape == Shape.ELLIPSE && points.length == 3) { let ry = Math.hypot(points[1][0] - points[0][0], points[1][1] - points[0][1]); let rx = Math.hypot(points[2][0] - points[0][0], points[2][1] - points[0][1]); row += ``; - } else if (this.shape == Shapes.ELLIPSE && points.length == 2) { + } else if (this.shape == Shape.ELLIPSE && points.length == 2) { let r = Math.hypot(points[1][0] - points[0][0], points[1][1] - points[0][1]); row += ``; - } else if (this.shape == Shapes.RECTANGLE && points.length == 2) { + } else if (this.shape == Shape.RECTANGLE && points.length == 2) { row += ``; - } else if (this.shape == Shapes.POLYGON && points.length >= 3) { + } else if (this.shape == Shape.POLYGON && points.length >= 3) { row += ``; - } else if (this.shape == Shapes.POLYLINE && points.length >= 2) { + } else if (this.shape == Shape.POLYLINE && points.length >= 2) { row += ` 0; this.redoButton.child.reactive = this.area.undoneElements.length > 0 || (this.area.elements.length && this.area.elements[this.area.elements.length - 1].canUndo); this.eraseButton.child.reactive = this.area.elements.length > 0; - this.smoothButton.child.reactive = this.area.elements.length > 0 && this.area.elements[this.area.elements.length - 1].shape == this.drawingTools.NONE; + this.smoothButton.child.reactive = this.area.elements.length > 0 && this.area.elements[this.area.elements.length - 1].shape == this.DrawingTool.NONE; this.saveButton.child.reactive = this.area.elements.length > 0; this.svgButton.child.reactive = this.area.elements.length > 0; this.saveDrawingSubMenuItem.setSensitive(this.area.elements.length > 0); }, _updateSectionVisibility: function() { - let [isText, isImage] = [this.area.currentTool == this.drawingTools.TEXT, this.area.currentTool == this.drawingTools.IMAGE]; + let [isText, isImage] = [this.area.currentTool == this.DrawingTool.TEXT, this.area.currentTool == this.DrawingTool.IMAGE]; this.lineSection.actor.visible = !isText && !isImage; this.fontSection.actor.visible = isText; this.imageSection.actor.visible = isImage; @@ -425,7 +425,7 @@ var DrawingMenu = new Lang.Class({ let item = new PopupMenu.PopupSubMenuMenuItem('', true); item.update = () => { item.label.set_text(DisplayStrings.Tool[this.area.currentTool]); - let toolName = this.drawingTools.getNameOf(this.area.currentTool); + let toolName = this.DrawingTool.getNameOf(this.area.currentTool); item.icon.set_gicon(Files.Icons[`TOOL_${toolName}`]); }; item.update(); @@ -435,7 +435,7 @@ var DrawingMenu = new Lang.Class({ GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { Object.keys(DisplayStrings.Tool).forEach(key => { let text = DisplayStrings.Tool[key]; - let toolName = this.drawingTools.getNameOf(key); + let toolName = this.DrawingTool.getNameOf(key); let subItemIcon = Files.Icons[`TOOL_${toolName}`]; let subItem = item.menu.addAction(text, () => { this.area.currentTool = Number(key); @@ -447,10 +447,10 @@ var DrawingMenu = new Lang.Class({ getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment); // change the display order of tools - if (key == this.drawingTools.POLYGON) - item.menu.moveMenuItem(subItem, Number(this.drawingTools.TEXT)); - else if (key == this.drawingTools.POLYLINE) - item.menu.moveMenuItem(subItem, Number(this.drawingTools.TEXT) + 1); + if (key == this.DrawingTool.POLYGON) + item.menu.moveMenuItem(subItem, Number(this.DrawingTool.TEXT)); + else if (key == this.DrawingTool.POLYLINE) + item.menu.moveMenuItem(subItem, Number(this.DrawingTool.TEXT) + 1); }); return GLib.SOURCE_REMOVE; }); @@ -680,7 +680,7 @@ var DrawingMenu = new Lang.Class({ let insertCallback = () => { this.area.currentImage = json.image; this.imageItem.update(); - this.area.currentTool = this.drawingTools.IMAGE; + this.area.currentTool = this.DrawingTool.IMAGE; this.toolItem.update(); this._updateSectionVisibility(); }; From 27a398d0e1a6c90ad7a0a1db05583f3f24828355 Mon Sep 17 00:00:00 2001 From: abakkk Date: Thu, 18 Feb 2021 09:07:12 +0100 Subject: [PATCH 06/12] add convenient marks for rotation and stretch transformations --- area.js | 1 + elements.js | 56 +++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/area.js b/area.js index ea0eebd..5849e72 100644 --- a/area.js +++ b/area.js @@ -330,6 +330,7 @@ var DrawingArea = new Lang.Class({ } cr.stroke(); + this.elements[i]._addMarks(cr); cr.restore(); } diff --git a/elements.js b/elements.js index c1249a8..84c45cf 100644 --- a/elements.js +++ b/elements.js @@ -64,6 +64,7 @@ const MIN_TRANSLATION_DISTANCE = 1; // px const MIN_ROTATION_ANGLE = Math.PI / 1000; // rad const MIN_DRAWING_SIZE = 3; // px const MIN_INTERMEDIATE_POINT_DISTANCE = 1; // px, the higher it is, the fewer points there will be +const MARK_COLOR = Clutter.Color.get_static(Clutter.StaticColor.BLUE); var DrawingElement = function(params) { return params.shape == Shape.TEXT ? new TextElement(params) : @@ -139,18 +140,6 @@ const _DrawingElement = new Lang.Class({ if (this.color) Clutter.cairo_set_source_color(cr, this.color); - if (this.showSymmetryElement) { - let transformation = this.lastTransformation; - setDummyStroke(cr); - if (transformation.type == Transformation.REFLECTION) { - cr.moveTo(transformation.startX, transformation.startY); - cr.lineTo(transformation.endX, transformation.endY); - } else { - cr.arc(transformation.endX, transformation.endY, INVERSION_CIRCLE_RADIUS, 0, 2 * Math.PI); - } - cr.stroke(); - } - if (this.line) { cr.setLineCap(this.line.lineCap); cr.setLineJoin(this.line.lineJoin); @@ -205,6 +194,43 @@ const _DrawingElement = new Lang.Class({ cr.identityMatrix(); }, + _addMarks: function(cr) { + if (this.showSymmetryElement) { + setDummyStroke(cr); + Clutter.cairo_set_source_color(cr, MARK_COLOR); + + let transformation = this.lastTransformation; + if (transformation.type == Transformation.REFLECTION) { + cr.moveTo(transformation.startX, transformation.startY); + cr.lineTo(transformation.endX, transformation.endY); + } else { + cr.arc(transformation.endX, transformation.endY, INVERSION_CIRCLE_RADIUS, 0, 2 * Math.PI); + } + cr.stroke(); + } + + if (this.showRotationCenter) { + setDummyStroke(cr); + Clutter.cairo_set_source_color(cr, MARK_COLOR); + + let center = this._getTransformedCenter(this.lastTransformation); + cr.arc(center[0], center[1], INVERSION_CIRCLE_RADIUS, 0, 2 * Math.PI); + cr.stroke(); + } + + if (this.showStretchAxes) { + setDummyStroke(cr); + Clutter.cairo_set_source_color(cr, MARK_COLOR); + + let center = this._getTransformedCenter(this.lastTransformation); + for (let i = 0; i <=1; i++) { + cr.moveTo(center[0] - 1000 * Math.cos(i * Math.PI / 2), center[1] - 1000 * Math.sin(i * Math.PI / 2)); + cr.lineTo(center[0] + 1000 * Math.cos(i * Math.PI / 2), center[1] + 1000 * Math.sin(i * Math.PI / 2)); + } + cr.stroke(); + } + }, + _drawCairo: function(cr, params) { let [points, shape] = [this.points, this.shape]; @@ -527,6 +553,10 @@ const _DrawingElement = new Lang.Class({ if (type == Transformation.REFLECTION || type == Transformation.INVERSION) this.showSymmetryElement = true; + else if (type == Transformation.ROTATION) + this.showRotationCenter = true; + else if (type == Transformation.STRETCH) + this.showStretchAxes = true; }, updateTransformation: function(x, y) { @@ -584,6 +614,8 @@ const _DrawingElement = new Lang.Class({ stopTransformation: function() { this.showSymmetryElement = false; + this.showRotationCenter = false; + this.showStretchAxes = false; // Clean transformations let transformation = this.lastTransformation; From f087570eb53d4dfad5dab00515cdfa9bb6d91660 Mon Sep 17 00:00:00 2001 From: abakkk Date: Fri, 19 Feb 2021 23:17:16 +0100 Subject: [PATCH 07/12] minor, param renaming "showTextRectangle" -> "showElementBounds" and "drawTextRectangle" -> "drawElementBounds". --- area.js | 8 ++++---- elements.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/area.js b/area.js index 5849e72..fbf14e4 100644 --- a/area.js +++ b/area.js @@ -317,8 +317,8 @@ var DrawingArea = new Lang.Class({ for (let i = 0; i < this.elements.length; i++) { cr.save(); - this.elements[i].buildCairo(cr, { showTextRectangle: this.grabbedElement && this.grabbedElement == this.elements[i], - drawTextRectangle: this.grabPoint ? true : false }); + this.elements[i].buildCairo(cr, { showElementBounds: this.grabbedElement && this.grabbedElement == this.elements[i], + drawElementBounds: this.grabPoint ? true : false }); if (this.grabPoint) this._searchElementToGrab(cr, this.elements[i]); @@ -336,7 +336,7 @@ var DrawingArea = new Lang.Class({ if (this.currentElement && this.currentElement.eraser) { this.currentElement.buildCairo(cr, { showTextCursor: this.textHasCursor, - showTextRectangle: this.currentElement.shape != Shape.TEXT || !this.isWriting, + showElementBounds: this.currentElement.shape != Shape.TEXT || !this.isWriting, dummyStroke: this.currentElement.fill && this.currentElement.line.lineWidth == 0 }); cr.stroke(); } @@ -347,7 +347,7 @@ var DrawingArea = new Lang.Class({ return; this.currentElement.buildCairo(cr, { showTextCursor: this.textHasCursor, - showTextRectangle: this.currentElement.shape != Shape.TEXT || !this.isWriting, + showElementBounds: this.currentElement.shape != Shape.TEXT || !this.isWriting, dummyStroke: this.currentElement.fill && this.currentElement.line.lineWidth == 0 }); cr.stroke(); }, diff --git a/elements.js b/elements.js index 84c45cf..bbbb1d2 100644 --- a/elements.js +++ b/elements.js @@ -809,11 +809,11 @@ const TextElement = new Lang.Class({ cr.fill(); } - if (params.showTextRectangle) { + if (params.showElementBounds) { cr.rectangle(this.x, this.y - this.height, this.textWidth, this.height * layout.get_line_count()); setDummyStroke(cr); - } else if (params.drawTextRectangle) { + } else if (params.drawElementBounds) { cr.rectangle(this.x, this.y - this.height, this.textWidth, this.height * layout.get_line_count()); // Only draw the rectangle to find the element, not to show it. @@ -937,10 +937,10 @@ const ImageElement = new Lang.Class({ cr.fill(); cr.restore(); - if (params.showTextRectangle) { + if (params.showElementBounds) { cr.rectangle(x, y, width, height); setDummyStroke(cr); - } else if (params.drawTextRectangle) { + } else if (params.drawElementBounds) { cr.rectangle(x, y, width, height); // Only draw the rectangle to find the element, not to show it. cr.setLineWidth(0); From d125d77d011c1c9f0c03f345e4ad4687dc1de2da Mon Sep 17 00:00:00 2001 From: abakkk Date: Thu, 18 Feb 2021 16:36:36 +0100 Subject: [PATCH 08/12] Gtk4 port Tested with Gtk 4.1.0. --- prefs.js | 398 +++++++++++++++++++++++++++++++-------------------- shortcuts.js | 7 +- 2 files changed, 248 insertions(+), 157 deletions(-) diff --git a/prefs.js b/prefs.js index 434c9ab..3b7f9f5 100644 --- a/prefs.js +++ b/prefs.js @@ -21,12 +21,12 @@ * along with this program. If not, see . */ -const Atk = imports.gi.Atk; const Gdk = imports.gi.Gdk; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; +const IS_GTK3 = imports.gi.versions.Gtk == 3; const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); @@ -39,27 +39,66 @@ const _ = function(string) { return ""; return gettext(string); }; -const _GTK = imports.gettext.domain('gtk30').gettext; +const _GTK = imports.gettext.domain(IS_GTK3 ? 'gtk30' : 'gtk40').gettext; const MARGIN = 10; -const ROWBOX_MARGIN_PARAMS = { margin_top: MARGIN / 2, margin_bottom: MARGIN / 2, margin_left: MARGIN, margin_right: MARGIN }; +const ROWBOX_MARGIN_PARAMS = { margin_top: MARGIN / 2, margin_bottom: MARGIN / 2, margin_start: MARGIN, margin_end: MARGIN, spacing: 4 }; const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_'); +if (IS_GTK3) { + Gtk.Container.prototype.append = Gtk.Container.prototype.add; + Gtk.Bin.prototype.set_child = Gtk.Container.prototype.add; +} + +const setAccessibleLabel = function(widget, label) { + if (IS_GTK3) + widget.get_accessible().set_name(label); + else + widget.update_property([Gtk.AccessibleProperty.LABEL], [label]); +}; + +const setAccessibleDescription = function(widget, description) { + if (IS_GTK3) + widget.get_accessible().set_description(description); + else + widget.update_property([Gtk.AccessibleProperty.DESCRIPTION], [description]); +}; + +const getChildrenOf = function(widget) { + if (IS_GTK3) { + return widget.get_children(); + } else { + let listModel = widget.observe_children(); + let i = 0; + let children = []; + let child; + while (child = listModel.get_item(i)) { + children.push(child); + i++; + } + return children; + } +}; + function init() { Convenience.initTranslations(); } function buildPrefsWidget() { let topStack = new TopStack(); - let switcher = new Gtk.StackSwitcher({halign: Gtk.Align.CENTER, visible: true, stack: topStack}); + let switcher = new Gtk.StackSwitcher({ halign: Gtk.Align.CENTER, visible: true, stack: topStack }); + GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { - let window = topStack.get_toplevel(); - let headerBar = window.get_titlebar(); - headerBar.custom_title = switcher; - return false; + if (IS_GTK3) + topStack.get_toplevel().get_titlebar().set_custom_title(switcher); + else + topStack.get_root().get_titlebar().set_title_widget(switcher); + + return GLib.SOURCE_REMOVE; }); - topStack.show_all(); + if (IS_GTK3) + topStack.show_all(); return topStack; } @@ -68,7 +107,7 @@ const TopStack = new GObject.Class({ Extends: Gtk.Stack, _init: function(params) { - this.parent({ transition_type: 1, transition_duration: 500, expand: true }); + this.parent({ transition_type: Gtk.StackTransitionType.CROSSFADE, transition_duration: 500 }); this.prefsPage = new PrefsPage(); // Translators: "Preferences" page in preferences this.add_titled(this.prefsPage, 'prefs', _("Preferences")); @@ -88,8 +127,8 @@ const AboutPage = new GObject.Class({ _init: function(params) { this.parent({ hscrollbar_policy: Gtk.PolicyType.NEVER }); - let vbox= new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, margin: MARGIN * 3 }); - this.add(vbox); + let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, margin_top: 3 * MARGIN, margin_bottom: 3 * MARGIN, margin_start: 3 * MARGIN, margin_end: 3 * MARGIN }); + this.set_child(vbox); // Translators: you are free to translate the extension name, that is displayed in About page, or not let name = " " + _("Draw On You Screen") + ""; @@ -102,21 +141,21 @@ const AboutPage = new GObject.Class({ let licenceLink = "https://www.gnu.org/licenses/old-licenses/gpl-2.0.html"; let licence = "" + _GTK("This program comes with absolutely no warranty.\nSee the %s for details.").format(licenceLink, licenceName) + ""; - let aboutLabel = new Gtk.Label({ wrap: true, justify: 2, use_markup: true, label: + let aboutLabel = new Gtk.Label({ wrap: true, justify: Gtk.Justification.CENTER, use_markup: true, label: name + "\n\n" + version + "\n\n" + description + "\n\n" + link + "\n\n" + licence + "\n" }); - vbox.add(aboutLabel); + vbox.append(aboutLabel); - let creditBox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, margin: 2 * MARGIN }); - let leftBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL }); - let rightBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL }); - let leftLabel = new Gtk.Label({ wrap: true, valign: 1, halign: 2, justify: 1, use_markup: true, label: "" + _GTK("Created by") + "" }); - let rightLabel = new Gtk.Label({ wrap: true, valign: 1, halign: 1, justify: 0, use_markup: true, label: "Abakkk" }); - leftBox.pack_start(leftLabel, false, false, 0); - rightBox.pack_start(rightLabel, false, false, 0); - creditBox.pack_start(leftBox, true, true, 5); - creditBox.pack_start(rightBox, true, true, 5); - vbox.add(creditBox); + let creditBox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, margin_top: 2 * MARGIN, margin_bottom: 2 * MARGIN, margin_start: 2 * MARGIN, margin_end: 2 * MARGIN, spacing: 5 }); + let leftBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, hexpand: true }); + let rightBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, hexpand: true }); + leftBox.append(new Gtk.Label({ wrap: true, valign: Gtk.Align.START, halign: Gtk.Align.END, justify: Gtk.Justification.RIGHT, + use_markup: true, label: "" + _GTK("Created by") + "" })); + rightBox.append(new Gtk.Label({ wrap: true, valign: Gtk.Align.START, halign: Gtk.Align.START, justify: Gtk.Justification.LEFT, + use_markup: true, label: "Abakkk" })); + creditBox.append(leftBox); + creditBox.append(rightBox); + vbox.append(creditBox); // Translators: add your name here or keep it empty, it will be displayed in about page, e.g. // msgstr "" @@ -124,12 +163,10 @@ const AboutPage = new GObject.Class({ // "translator2\n" // "translator3" if (_("translator-credits") != "translator-credits" && _("translator-credits") != "") { - leftBox.pack_start(new Gtk.Label(), false, false, 0); - rightBox.pack_start(new Gtk.Label(), false, false, 0); - leftLabel = new Gtk.Label({ wrap: true, valign: 1, halign: 2, justify: 1, use_markup: true, label: "" + _GTK("Translated by") + "" }); - rightLabel = new Gtk.Label({ wrap: true, valign: 1, halign: 1, justify: 0, use_markup: true, label: "" + _("translator-credits") + "" }); - leftBox.pack_start(leftLabel, false, false, 0); - rightBox.pack_start(rightLabel, false, false, 0); + leftBox.append(new Gtk.Label()); + rightBox.append(new Gtk.Label()); + leftBox.append(new Gtk.Label({ wrap: true, valign: Gtk.Align.START, halign: Gtk.Align.END, justify: 1, use_markup: true, label: "" + _GTK("Translated by") + "" })); + rightBox.append(new Gtk.Label({ wrap: true, valign: Gtk.Align.START, halign: Gtk.Align.START, justify: 0, use_markup: true, label: "" + _("translator-credits") + "" })); } } }); @@ -144,44 +181,46 @@ const DrawingPage = new GObject.Class({ this.settings = Convenience.getSettings(Me.metadata['settings-schema'] + '.drawing'); this.schema = this.settings.settings_schema; - let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, margin: 3 * MARGIN, spacing: 3 * MARGIN }); - this.add(box); + let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, margin_top: 3 * MARGIN, margin_bottom: 3 * MARGIN, margin_start: 3 * MARGIN, margin_end: 3 * MARGIN, spacing: 3 * MARGIN }); + this.set_child(box); let palettesFrame = new Frame({ label: _("Palettes") }); - box.add(palettesFrame); + box.append(palettesFrame); let palettesFrameBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL }); - palettesFrame.add(palettesFrameBox); + palettesFrame.set_child(palettesFrameBox); let palettesScrolledWindow = new Gtk.ScrolledWindow({ vscrollbar_policy: Gtk.PolicyType.NEVER }); - palettesFrameBox.add(palettesScrolledWindow); - let palettesViewport = new Gtk.Viewport({ margin_top: MARGIN / 2, margin_bottom: MARGIN / 2 }); - palettesScrolledWindow.add(palettesViewport); + palettesFrameBox.append(palettesScrolledWindow); this.palettesListBox = new Gtk.ListBox({ selection_mode: 0, hexpand: true }); this.palettesListBox.get_style_context().add_class('background'); - this.palettesListBox.get_accessible().set_name(this.schema.get_key('palettes').get_summary()); - this.palettesListBox.get_accessible().set_description(this.schema.get_key('palettes').get_description()); - palettesViewport.add(this.palettesListBox); + setAccessibleLabel(this.palettesListBox, this.schema.get_key('palettes').get_summary()); + setAccessibleDescription(this.palettesListBox, this.schema.get_key('palettes').get_description()); + palettesScrolledWindow.set_child(this.palettesListBox); + palettesScrolledWindow.get_child().set_margin_top(MARGIN / 2); + palettesScrolledWindow.get_child().set_margin_bottom(MARGIN / 2); this.settings.connect('changed::palettes', this._updatePalettes.bind(this)); this._updatePalettes(); let addBox = new Gtk.Box(ROWBOX_MARGIN_PARAMS); - let addButton = Gtk.Button.new_from_icon_name('list-add-symbolic', Gtk.IconSize.BUTTON); + let addButton = IS_GTK3 ? Gtk.Button.new_from_icon_name('list-add-symbolic', Gtk.IconSize.BUTTON) : Gtk.Button.new_from_icon_name('list-add-symbolic'); addButton.set_tooltip_text(_("Add a new palette")); - addBox.pack_start(addButton, true, true, 4); + addButton.set_hexpand(true); + addBox.append(addButton); addButton.connect('clicked', this._addNewPalette.bind(this)); - let importButton = Gtk.Button.new_from_icon_name('document-open-symbolic', Gtk.IconSize.BUTTON); + let importButton = IS_GTK3 ? Gtk.Button.new_from_icon_name('document-open-symbolic', Gtk.IconSize.BUTTON) : Gtk.Button.new_from_icon_name('document-open-symbolic'); importButton.set_tooltip_text(_GTK("Select a File")); - addBox.pack_start(importButton, true, true, 4); + importButton.set_hexpand(true); + addBox.append(importButton); importButton.connect('clicked', this._importPalette.bind(this)); - palettesFrameBox.add(addBox); + palettesFrameBox.append(addBox); let areaFrame = new Frame({ label: _("Area") }); - box.add(areaFrame); + box.append(areaFrame); let areaListBox = new Gtk.ListBox({ selection_mode: 0, hexpand: true, margin_top: MARGIN / 2, margin_bottom: MARGIN / 2 }); areaListBox.get_style_context().add_class('background'); - areaFrame.add(areaListBox); + areaFrame.set_child(areaListBox); let squareAreaRow = new PrefRow({ label: this.schema.get_key('square-area-size').get_summary() }); let squareAreaAutoButton = new Gtk.CheckButton({ label: _("Auto"), @@ -196,7 +235,7 @@ const DrawingPage = new GObject.Class({ squareAreaAutoButton.bind_property('active', squareAreaSizeButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN); squareAreaRow.addWidget(squareAreaAutoButton); squareAreaRow.addWidget(squareAreaSizeButton); - areaListBox.add(squareAreaRow); + areaListBox.insert(squareAreaRow, -1); let backgroundColorRow = new PrefRow({ label: this.schema.get_key('background-color').get_summary() }); let backgroundColorButton = new ColorStringButton({ use_alpha: true, show_editor: true, @@ -204,7 +243,7 @@ const DrawingPage = new GObject.Class({ tooltip_text: this.schema.get_key('background-color').get_description() }); this.settings.bind('background-color', backgroundColorButton, 'color-string', 0); backgroundColorRow.addWidget(backgroundColorButton); - areaListBox.add(backgroundColorRow); + areaListBox.insert(backgroundColorRow, -1); let gridLineRow = new PrefRow({ label: _("Grid overlay line") }); let gridLineAutoButton = new Gtk.CheckButton({ label: _("Auto"), @@ -226,7 +265,7 @@ const DrawingPage = new GObject.Class({ gridLineRow.addWidget(gridLineAutoButton); gridLineRow.addWidget(gridLineWidthButton); gridLineRow.addWidget(gridLineSpacingButton); - areaListBox.add(gridLineRow); + areaListBox.insert(gridLineRow, -1); let gridColorRow = new PrefRow({ label: this.schema.get_key('grid-color').get_summary() }); let gridColorButton = new ColorStringButton({ use_alpha: true, show_editor: true, @@ -234,14 +273,14 @@ const DrawingPage = new GObject.Class({ tooltip_text: this.schema.get_key('grid-color').get_description() }); this.settings.bind('grid-color', gridColorButton, 'color-string', 0); gridColorRow.addWidget(gridColorButton); - areaListBox.add(gridColorRow); + areaListBox.insert(gridColorRow, -1); let toolsFrame = new Frame({ label: _("Tools") }); - box.add(toolsFrame); + box.append(toolsFrame); let toolsListBox = new Gtk.ListBox({ selection_mode: 0, hexpand: true, margin_top: MARGIN / 2, margin_bottom: MARGIN / 2 }); toolsListBox.get_style_context().add_class('background'); - toolsFrame.add(toolsListBox); + toolsFrame.set_child(toolsListBox); let dashArrayRow = new PrefRow({ label: _("Dash array") }); let dashArrayAutoButton = new Gtk.CheckButton({ label: _("Auto"), @@ -263,7 +302,7 @@ const DrawingPage = new GObject.Class({ dashArrayRow.addWidget(dashArrayAutoButton); dashArrayRow.addWidget(dashArrayOnButton); dashArrayRow.addWidget(dashArrayOffButton); - toolsListBox.add(dashArrayRow); + toolsListBox.insert(dashArrayRow, -1); let dashOffsetRow = new PrefRow({ label: this.schema.get_key('dash-offset').get_summary() }); let dashOffsetButton = new PixelSpinButton({ width_chars: 5, digits: 1, step: 0.1, @@ -272,7 +311,7 @@ const DrawingPage = new GObject.Class({ tooltip_text: this.schema.get_key('dash-offset').get_description() }); this.settings.bind('dash-offset', dashOffsetButton, 'value', 0); dashOffsetRow.addWidget(dashOffsetButton); - toolsListBox.add(dashOffsetRow); + toolsListBox.insert(dashOffsetRow, -1); let imageLocationRow = new PrefRow({ label: this.schema.get_key('image-location').get_summary() }); let imageLocationButton = new FileChooserButton({ action: Gtk.FileChooserAction.SELECT_FOLDER, @@ -280,19 +319,19 @@ const DrawingPage = new GObject.Class({ tooltip_text: this.schema.get_key('image-location').get_description() }); this.settings.bind('image-location', imageLocationButton, 'location', 0); imageLocationRow.addWidget(imageLocationButton); - toolsListBox.add(imageLocationRow); + toolsListBox.insert(imageLocationRow, -1); let resetButton = new Gtk.Button({ label: _("Reset settings"), halign: Gtk.Align.CENTER }); resetButton.get_style_context().add_class('destructive-action'); resetButton.connect('clicked', () => this.schema.list_keys().forEach(key => this.settings.reset(key))); - box.add(resetButton); + box.append(resetButton); }, _updatePalettes: function() { this.palettes = this.settings.get_value('palettes').deep_unpack(); - this.palettesListBox.get_children().slice(this.palettes.length) + getChildrenOf(this.palettesListBox).slice(this.palettes.length) .forEach(row => this.palettesListBox.remove(row)); - let paletteBoxes = this.palettesListBox.get_children().map(row => row.get_child()); + let paletteBoxes = getChildrenOf(this.palettesListBox).map(row => row.get_child()); this.palettes.forEach((palette, paletteIndex) => { let [name, colors] = palette; @@ -300,22 +339,24 @@ const DrawingPage = new GObject.Class({ if (paletteBoxes[paletteIndex]) { paletteBox = paletteBoxes[paletteIndex]; - let nameEntry = paletteBox.get_children()[0]; + let nameEntry = getChildrenOf(paletteBox)[0]; if (nameEntry.get_text() !== _(name)) { GObject.signal_handler_block(nameEntry, nameEntry.paletteNameChangedHandler); nameEntry.set_text(_(name)); GObject.signal_handler_unblock(nameEntry, nameEntry.paletteNameChangedHandler); } } else { - let nameEntry = new Gtk.Entry({ text: name, halign: Gtk.Align.START, tooltip_text: _("Rename the palette") }); + let nameEntry = new Gtk.Entry({ text: name, halign: Gtk.Align.START, tooltip_text: _("Rename the palette"), hexpand: true }); nameEntry.paletteNameChangedHandler = nameEntry.connect('changed', this._onPaletteNameChanged.bind(this, paletteIndex)); - let removeButton = Gtk.Button.new_from_icon_name('list-remove-symbolic', Gtk.IconSize.BUTTON); + // Minimum size, for Gtk4 + nameEntry.set_size_request(nameEntry.get_preferred_size()[1].width, -1); + let removeButton = IS_GTK3 ? Gtk.Button.new_from_icon_name('list-remove-symbolic', Gtk.IconSize.BUTTON) : Gtk.Button.new_from_icon_name('list-remove-symbolic'); removeButton.set_tooltip_text(_("Remove the palette")); removeButton.connect('clicked', this._removePalette.bind(this, paletteIndex)); paletteBox = new Gtk.Box(ROWBOX_MARGIN_PARAMS); - paletteBox.pack_start(nameEntry, true, true, 4); - paletteBox.pack_start(new Gtk.Box({ spacing: 4 }), false, false, 4); - paletteBox.pack_start(removeButton, false, false, 4); + paletteBox.append(nameEntry); + paletteBox.append(new Gtk.Box({ spacing: 4 })); + paletteBox.append(removeButton); this.palettesListBox.insert(paletteBox, paletteIndex); paletteBox.get_parent().set_activatable(false); } @@ -323,8 +364,8 @@ const DrawingPage = new GObject.Class({ while (colors.length < 9) colors.push('transparent'); - let colorsBox = paletteBox.get_children()[1]; - let colorButtons = colorsBox.get_children(); + let colorsBox = getChildrenOf(paletteBox)[1]; + let colorButtons = getChildrenOf(colorsBox); colors.forEach((color, colorIndex) => { let [colorString, displayName] = color.split(':'); if (colorButtons[colorIndex]) { @@ -335,11 +376,12 @@ const DrawingPage = new GObject.Class({ use_alpha: true, show_editor: true, halign: Gtk.Align.START, hexpand: false }); colorButton.connect('notify::color-string', this._onPaletteColorChanged.bind(this, paletteIndex, colorIndex)); - colorsBox.add(colorButton); + colorsBox.append(colorButton); } }); - paletteBox.show_all(); + if (IS_GTK3) + paletteBox.show_all(); }); }, @@ -367,17 +409,31 @@ const DrawingPage = new GObject.Class({ }, _importPalette: function() { - let dialog = new Gtk.FileChooserNative({ action: Gtk.FileChooserAction.OPEN, title: _GTK("Select a File") }); + let dialog = new Gtk.FileChooserDialog({ + title: _GTK("Select a File"), + action: Gtk.FileChooserAction.OPEN, + modal: true, + transient_for: IS_GTK3 ? this.get_toplevel() : this.get_root(), + }); + dialog.add_button(_GTK("_Cancel"), Gtk.ResponseType.CANCEL); + dialog.add_button(_GTK("_Open"), Gtk.ResponseType.ACCEPT); + let filter = new Gtk.FileFilter(); filter.set_name("GIMP Palette (*.gpl)"); filter.add_pattern('*.gpl'); dialog.add_filter(filter); - if (dialog.run() == Gtk.ResponseType.ACCEPT) { - let file = dialog.get_file(); - let palettes = GimpPaletteParser.parseFile(file); - palettes.forEach(palette => this.palettes.push(palette)); - this._savePalettes(); - } + + dialog.connect('response', (dialog, response) => { + if (response == Gtk.ResponseType.ACCEPT) { + let file = dialog.get_file(); + let palettes = GimpPaletteParser.parseFile(file); + palettes.forEach(palette => this.palettes.push(palette)); + this._savePalettes(); + } + dialog.destroy(); + }); + + dialog.show(); }, _removePalette: function(paletteIndex) { @@ -397,24 +453,24 @@ const PrefsPage = new GObject.Class({ let schema = settings.settings_schema; let internalShortcutSettings = Convenience.getSettings(Me.metadata['settings-schema'] + '.internal-shortcuts'); - let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, margin: MARGIN * 3, spacing: 3 * MARGIN }); - this.add(box); + let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, margin_top: 3 * MARGIN, margin_bottom: 3 * MARGIN, margin_start: 3 * MARGIN, margin_end: 3 * MARGIN, spacing: 3 * MARGIN }); + this.set_child(box); let globalFrame = new Frame({ label: _("Global") }); - box.add(globalFrame); + box.append(globalFrame); let listBox = new Gtk.ListBox({ selection_mode: 0, hexpand: true, margin_top: MARGIN, margin_bottom: MARGIN / 2 }); listBox.get_style_context().add_class('background'); - globalFrame.add(listBox); + globalFrame.set_child(listBox); Shortcuts.GLOBAL_KEYBINDINGS.forEach((settingKeys, index) => { if (index) - listBox.add(new Gtk.Box(ROWBOX_MARGIN_PARAMS)); + listBox.insert(new Gtk.Box(ROWBOX_MARGIN_PARAMS), -1); let globalKeybindingsRow = new Gtk.ListBoxRow({ activatable: false }); let globalKeybindingsWidget = new KeybindingsWidget(settingKeys, settings); - globalKeybindingsRow.add(globalKeybindingsWidget); - listBox.add(globalKeybindingsRow); + globalKeybindingsRow.set_child(globalKeybindingsWidget); + listBox.insert(globalKeybindingsRow, -1); }); let persistentOverTogglesKey = schema.get_key('persistent-over-toggles'); @@ -422,7 +478,7 @@ const PrefsPage = new GObject.Class({ let persistentOverTogglesSwitch = new Gtk.Switch(); settings.bind('persistent-over-toggles', persistentOverTogglesSwitch, 'active', 0); persistentOverTogglesRow.addWidget(persistentOverTogglesSwitch, true); - listBox.add(persistentOverTogglesRow); + listBox.insert(persistentOverTogglesRow, -1); let persistentOverRestartsKey = schema.get_key('persistent-over-restarts'); let persistentOverRestartsRow = new PrefRow({ label: persistentOverRestartsKey.get_summary(), desc: persistentOverRestartsKey.get_description() }); @@ -430,7 +486,7 @@ const PrefsPage = new GObject.Class({ settings.bind('persistent-over-restarts', persistentOverRestartsSwitch, 'active', 0); persistentOverRestartsRow.addWidget(persistentOverRestartsSwitch, true); persistentOverTogglesSwitch.bind_property('active', persistentOverRestartsSwitch, 'sensitive', GObject.BindingFlags.SYNC_CREATE); - listBox.add(persistentOverRestartsRow); + listBox.insert(persistentOverRestartsRow, -1); let desktopKey = schema.get_key('drawing-on-desktop'); let desktopRow = new PrefRow({ label: desktopKey.get_summary(), desc: desktopKey.get_description() }); @@ -438,56 +494,53 @@ const PrefsPage = new GObject.Class({ settings.bind('drawing-on-desktop', desktopSwitch, 'active', 0); desktopRow.addWidget(desktopSwitch, true); persistentOverTogglesSwitch.bind_property('active', desktopSwitch, 'sensitive', GObject.BindingFlags.SYNC_CREATE); - listBox.add(desktopRow); + listBox.insert(desktopRow, -1); let osdKey = schema.get_key('osd-disabled'); let osdRow = new PrefRow({ label: osdKey.get_summary(), desc: osdKey.get_description() }); let osdSwitch = new Gtk.Switch(); settings.bind('osd-disabled', osdSwitch, 'active', 0); osdRow.addWidget(osdSwitch, true); - listBox.add(osdRow); + listBox.insert(osdRow, -1); let indicatorKey = schema.get_key('indicator-disabled'); let indicatorRow = new PrefRow({ label: indicatorKey.get_summary(), desc: indicatorKey.get_description() }); let indicatorSwitch = new Gtk.Switch(); settings.bind('indicator-disabled', indicatorSwitch, 'active', 0); indicatorRow.addWidget(indicatorSwitch, true); - listBox.add(indicatorRow); + listBox.insert(indicatorRow, -1); let internalFrame = new Frame({ label: _("Internal"), desc: _("In drawing mode") }); - box.add(internalFrame); + box.append(internalFrame); listBox = new Gtk.ListBox({ selection_mode: 0, hexpand: true, margin_top: MARGIN, margin_bottom: MARGIN }); listBox.get_style_context().add_class('background'); - internalFrame.add(listBox); + internalFrame.set_child(listBox); Shortcuts.OTHERS.forEach((pairs, index) => { if (index) - listBox.add(new Gtk.Box(ROWBOX_MARGIN_PARAMS)); + listBox.insert(new Gtk.Box(ROWBOX_MARGIN_PARAMS), -1); pairs.forEach(pair => { let [action, shortcut] = pair; - let otherBox = new Gtk.Box({ margin_left: MARGIN, margin_right: MARGIN }); - let otherLabel = new Gtk.Label({ label: action, use_markup: true }); - otherLabel.set_halign(1); - let otherLabel2 = new Gtk.Label({ label: shortcut }); - otherBox.pack_start(otherLabel, true, true, 4); - otherBox.pack_start(otherLabel2, false, false, 4); - listBox.add(otherBox); + let otherBox = new Gtk.Box({ margin_start: MARGIN, margin_end: MARGIN, spacing: 4 }); + otherBox.append(new Gtk.Label({ label: action, use_markup: true, halign: Gtk.Align.START, hexpand: true })); + otherBox.append(new Gtk.Label({ label: shortcut })); + listBox.insert(otherBox, -1); }); }); - listBox.add(new Gtk.Box(ROWBOX_MARGIN_PARAMS)); + listBox.insert(new Gtk.Box(ROWBOX_MARGIN_PARAMS), -1); Shortcuts.INTERNAL_KEYBINDINGS.forEach((settingKeys, index) => { if (index) - listBox.add(new Gtk.Box(ROWBOX_MARGIN_PARAMS)); + listBox.insert(new Gtk.Box(ROWBOX_MARGIN_PARAMS), -1); let internalKeybindingsWidget = new KeybindingsWidget(settingKeys, internalShortcutSettings); - listBox.add(internalKeybindingsWidget); + listBox.insert(internalKeybindingsWidget, -1); }); - listBox.get_children().forEach(row => row.set_activatable(false)); + getChildrenOf(listBox).forEach(row => row.set_activatable(false)); let resetButton = new Gtk.Button({ label: _("Reset settings"), halign: Gtk.Align.CENTER }); resetButton.get_style_context().add_class('destructive-action'); @@ -495,7 +548,7 @@ const PrefsPage = new GObject.Class({ internalShortcutSettings.settings_schema.list_keys().forEach(key => internalShortcutSettings.reset(key)); settings.settings_schema.list_keys().forEach(key => settings.reset(key)); }); - box.add(resetButton); + box.append(resetButton); } }); @@ -504,12 +557,12 @@ const Frame = new GObject.Class({ Extends: Gtk.Frame, _init: function(params) { - let labelWidget = new Gtk.Label({ margin_bottom: MARGIN / 2, use_markup: true, label: `${params.label}` }); - this.parent({ label_yalign: 1.0, label_widget: labelWidget }); + let labelWidget = new Gtk.Label({ use_markup: true, label: `${params.label}` }); + this.parent({ label_widget: labelWidget }); if (params.desc) { labelWidget.set_tooltip_text(params.desc); - this.get_accessible().set_description(params.desc); + setAccessibleDescription(this, params.desc); } } }); @@ -522,43 +575,43 @@ const PrefRow = new GObject.Class({ this.parent({ activatable: false }); let hbox = new Gtk.Box(ROWBOX_MARGIN_PARAMS); - this.add(hbox); + this.set_child(hbox); - let labelBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL }); - hbox.pack_start(labelBox, true, true, 4); + let labelBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, hexpand: true }); + hbox.append(labelBox); this.widgetBox = new Gtk.Box({ spacing: 4 }); - hbox.pack_start(this.widgetBox, false, false, 4); + hbox.append(this.widgetBox); - this.label = new Gtk.Label({ use_markup: true, label: params.label, halign: Gtk.Align.START }); - labelBox.pack_start(this.label, true, true, 0); + this.label = new Gtk.Label({ use_markup: true, label: params.label, halign: Gtk.Align.START, vexpand: true }); + labelBox.append(this.label); if (params.desc) { - this.desc = new Gtk.Label({ use_markup: true, label: `${params.desc}`, halign: Gtk.Align.START, wrap: true, xalign: 0 }); + this.desc = new Gtk.Label({ use_markup: true, label: `${params.desc}`, halign: Gtk.Align.START, wrap: true, xalign: 0, vexpand: true }); this.desc.get_style_context().add_class('dim-label'); - labelBox.pack_start(this.desc, true, true, 0); + labelBox.append(this.desc); this.widgetBox.set_valign(Gtk.Align.START); } }, addWidget: function(widget, setRelationship) { - this.widgetBox.add(widget); + this.widgetBox.append(widget); if (widget.name) - widget.get_accessible().set_name(widget.name); + setAccessibleLabel(widget, widget.name); - if (setRelationship) { - this.label.get_accessible().add_relationship(Atk.RelationType.LABEL_FOR, widget.get_accessible()); - widget.get_accessible().add_relationship(Atk.RelationType.LABELLED_BY, this.label.get_accessible()); + if (setRelationship && IS_GTK3) { + this.label.get_accessible().add_relationship(imports.gi.Atk.RelationType.LABEL_FOR, widget.get_accessible()); + widget.get_accessible().add_relationship(imports.gi.Atk.RelationType.LABELLED_BY, this.label.get_accessible()); if (this.desc) { - this.desc.get_accessible().add_relationship(Atk.RelationType.DESCRIPTION_FOR, widget.get_accessible()); - widget.get_accessible().add_relationship(Atk.RelationType.DESCRIBED_BY, this.desc.get_accessible()); + this.desc.get_accessible().add_relationship(imports.gi.Atk.RelationType.DESCRIPTION_FOR, widget.get_accessible()); + widget.get_accessible().add_relationship(imports.gi.Atk.RelationType.DESCRIBED_BY, this.desc.get_accessible()); } } } }); -const PixelSpinButton = new GObject.Class({ +const PixelSpinButton = new GObject.Class(Object.assign({ Name: `${UUID}-PixelSpinButton`, Extends: Gtk.SpinButton, Properties: { @@ -584,17 +637,22 @@ const PixelSpinButton = new GObject.Class({ this.adjustment.set_page_increment(step * 10); }, - // Add 'px' unit. - vfunc_output: function() { - this.text = _("%f px").format(Number(this.value).toFixed(2)); - return true; - }, - + vfunc_constructed() { + this.parent(); + + // No virtual functions with Gtk4 :/. + // Add 'px' unit. + this.connect('output', spinButton => { + this.text = _("%f px").format(Number(spinButton.value).toFixed(2)); + return true; + }); + } +}, IS_GTK3 ? { // Prevent accidental scrolling. vfunc_scroll_event: function(event) { return this.has_focus ? this.parent(event) : Gdk.EVENT_PROPAGATE; } -}); +} : {})); // A color button that can be easily bound with a color string setting. const ColorStringButton = new GObject.Class({ @@ -617,49 +675,75 @@ const ColorStringButton = new GObject.Class({ this.set_rgba(newRgba); }, - // Do nothing if the new color is equivalent to the old color (e.g. "black" and "rgb(0,0,0)"). - vfunc_color_set(args) { - let oldRgba = new Gdk.RGBA(); - oldRgba.parse(this.color_string); + vfunc_constructed() { + this.parent(); - if (!this.rgba.equal(oldRgba)) { - this._color_string = this.rgba.to_string(); - this.notify('color-string'); - } + // No virtual functions with Gtk4 :/. + // Do nothing if the new color is equivalent to the old color (e.g. "black" and "rgb(0,0,0)"). + this.connect('color-set', colorButton => { + let oldRgba = new Gdk.RGBA(); + oldRgba.parse(colorButton.color_string); + + if (!this.rgba.equal(oldRgba)) { + this._color_string = colorButton.rgba.to_string(); + this.notify('color-string'); + } + }); } }); const FileChooserButton = new GObject.Class({ Name: `${UUID}-FileChooserButton`, - Extends: Gtk.FileChooserButton, + Extends: Gtk.Button, Properties: { + 'action': GObject.ParamSpec.enum('action', 'action', 'action', + GObject.ParamFlags.READWRITE, + Gtk.FileChooserAction.$gtype, + Gtk.FileChooserAction.SELECT_FOLDER), + 'location': GObject.ParamSpec.string('location', 'location', 'location', GObject.ParamFlags.READWRITE, '') }, get location() { - return this.get_file().get_path(); + return this._location || ""; }, set location(location) { - if (!location) { - this.unselect_all(); - if (this.get_file()) - this.set_file(Gio.File.new_for_path('aFileThatDoesNotExist')); - return; + if (this._location != location) { + this._location = location; + this.label = location ? + Gio.File.new_for_commandline_arg(location).query_info('standard::display-name', Gio.FileQueryInfoFlags.NONE, null).get_display_name() : + _GTK("(None)"); + + this.notify('location'); } - - let file = Gio.File.new_for_commandline_arg(location); - if (file.query_exists(null)) - this.set_file(file); }, - vfunc_file_set: function(args) { - this.notify('location'); + vfunc_clicked: function() { + let dialog = new Gtk.FileChooserDialog({ + title: _(this.name), + action: this.action, + modal: true, + transient_for: IS_GTK3 ? this.get_toplevel() : this.get_root(), + }); + dialog.add_button(_GTK("_Cancel"), Gtk.ResponseType.CANCEL); + dialog.add_button(_GTK("_Select"), Gtk.ResponseType.ACCEPT); + + if (this.location) + dialog.set_file(Gio.File.new_for_commandline_arg(this.location)); + + dialog.connect('response', (dialog, response) => { + if (response == Gtk.ResponseType.ACCEPT) + this.location = dialog.get_file().get_path(); + dialog.destroy(); + }); + + dialog.show(); } }); -// this code comes from Sticky Notes View by Sam Bull, https://extensions.gnome.org/extension/568/notes/ +// From Sticky Notes View by Sam Bull, https://extensions.gnome.org/extension/568/notes/ const KeybindingsWidget = new GObject.Class({ Name: `${UUID}-KeybindingsWidget`, Extends: Gtk.Box, @@ -713,7 +797,7 @@ const KeybindingsWidget = new GObject.Class({ let [success, iterator ] = this._store.get_iter_from_string(iter); - if(!success) { + if (!success) { printerr("Can't change keybinding"); } @@ -745,7 +829,7 @@ const KeybindingsWidget = new GObject.Class({ this._tree_view.columns_autosize(); this._tree_view.set_headers_visible(false); - this.add(this._tree_view); + this.append(this._tree_view); this.keybinding_column = keybinding_column; this.action_column = action_column; @@ -768,9 +852,11 @@ const KeybindingsWidget = new GObject.Class({ this._store.clear(); this._settingKeys.forEach(settingKey => { - let [key, mods] = Gtk.accelerator_parse( - this._settings.get_strv(settingKey)[0] || '' - ); + let success_, key, mods; + if (IS_GTK3) + [key, mods] = Gtk.accelerator_parse(this._settings.get_strv(settingKey)[0] || ''); + else + [success_, key, mods] = Gtk.accelerator_parse(this._settings.get_strv(settingKey)[0] || ''); let iter = this._store.append(); this._store.set(iter, diff --git a/shortcuts.js b/shortcuts.js index 2b2d1ac..73ef95b 100644 --- a/shortcuts.js +++ b/shortcuts.js @@ -22,6 +22,7 @@ */ const Gtk = imports.gi.Gtk; +const IS_GTK3 = imports.gi.versions.Gtk == 3; const GS_VERSION = imports.misc.config.PACKAGE_VERSION; const ExtensionUtils = imports.misc.extensionUtils; @@ -32,7 +33,11 @@ const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext; const internalShortcutsSchema = Convenience.getSettings(Me.metadata['settings-schema'] + '.internal-shortcuts').settings_schema; const getKeyLabel = function(accel) { - let [keyval, mods] = Gtk.accelerator_parse(accel); + let success_, keyval, mods; + if (IS_GTK3) + [keyval, mods] = Gtk.accelerator_parse(accel); + else + [success_, keyval, mods] = Gtk.accelerator_parse(accel); return Gtk.accelerator_get_label(keyval, mods); }; From 137742d471a9d537d30f26107601833bb36fa97b Mon Sep 17 00:00:00 2001 From: abakkk Date: Fri, 19 Feb 2021 22:44:10 +0100 Subject: [PATCH 09/12] adapt to Clutter API changes device.get_coords() has been removed and seat.query_state() is unusable with null sequences. --- area.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/area.js b/area.js index fbf14e4..f368a76 100644 --- a/area.js +++ b/area.js @@ -735,9 +735,17 @@ var DrawingArea = new Lang.Class({ // Minimum time between two motion events is about 33 ms. // Add intermediate points to make quick free drawings smoother. this.motionTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, MOTION_TIME, () => { - let [success, coords] = device.get_coords(sequence); - if (!success) - return GLib.SOURCE_CONTINUE; + let success, coords; + if (device.get_coords) { + [success, coords] = device.get_coords(sequence); + if (!success) + return GLib.SOURCE_CONTINUE; + } else { + // GS 40+, device.get_coords() has been removed + // and seat.query_state() is unusable with null sequences. + let pointer = global.get_pointer(); + coords = { x: pointer[0], y: pointer[1] }; + } let [s, x, y] = this._transformStagePoint(coords.x, coords.y); if (!s) From e470e2f40dfeaf767bb61e8f9bbd2ea18bfac709 Mon Sep 17 00:00:00 2001 From: abakkk Date: Sat, 20 Feb 2021 00:01:08 +0100 Subject: [PATCH 10/12] fixes for old GS versions --- extension.js | 2 +- prefs.js | 9 +++++---- shortcuts.js | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/extension.js b/extension.js index 3b96f5c..2535a5e 100644 --- a/extension.js +++ b/extension.js @@ -171,7 +171,7 @@ const AreaManager = new Lang.Class({ let loadPersistent = i == Main.layoutManager.primaryIndex && this.persistentOverRestarts; // Some utils for the drawing area menus. let areaManagerUtils = { - getHiddenList: () => this.hiddenList, + getHiddenList: () => this.hiddenList || null, togglePanelAndDockOpacity: this.togglePanelAndDockOpacity.bind(this), openPreferences: this.openPreferences.bind(this) }; diff --git a/prefs.js b/prefs.js index 3b7f9f5..2c93299 100644 --- a/prefs.js +++ b/prefs.js @@ -26,7 +26,7 @@ const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const GObject = imports.gi.GObject; const Gtk = imports.gi.Gtk; -const IS_GTK3 = imports.gi.versions.Gtk == 3; +const IS_GTK3 = Gtk.get_major_version() == 3; const ExtensionUtils = imports.misc.extensionUtils; const Me = ExtensionUtils.getCurrentExtension(); @@ -72,7 +72,7 @@ const getChildrenOf = function(widget) { let i = 0; let children = []; let child; - while (child = listModel.get_item(i)) { + while (!!(child = listModel.get_item(i))) { children.push(child); i++; } @@ -107,7 +107,8 @@ const TopStack = new GObject.Class({ Extends: Gtk.Stack, _init: function(params) { - this.parent({ transition_type: Gtk.StackTransitionType.CROSSFADE, transition_duration: 500 }); + this.parent({ transition_type: Gtk.StackTransitionType.CROSSFADE, transition_duration: 500, hexpand: true, vexpand: true }); + this.prefsPage = new PrefsPage(); // Translators: "Preferences" page in preferences this.add_titled(this.prefsPage, 'prefs', _("Preferences")); @@ -710,7 +711,7 @@ const FileChooserButton = new GObject.Class({ }, set location(location) { - if (this._location != location) { + if (!this._location || this._location != location) { this._location = location; this.label = location ? Gio.File.new_for_commandline_arg(location).query_info('standard::display-name', Gio.FileQueryInfoFlags.NONE, null).get_display_name() : diff --git a/shortcuts.js b/shortcuts.js index 73ef95b..9b3b5c2 100644 --- a/shortcuts.js +++ b/shortcuts.js @@ -22,7 +22,7 @@ */ const Gtk = imports.gi.Gtk; -const IS_GTK3 = imports.gi.versions.Gtk == 3; +const IS_GTK3 = Gtk.get_major_version() == 3; const GS_VERSION = imports.misc.config.PACKAGE_VERSION; const ExtensionUtils = imports.misc.extensionUtils; From 1a993dca38521492938fdc605fb6803fa8984463 Mon Sep 17 00:00:00 2001 From: abakkk Date: Sat, 20 Feb 2021 01:13:39 +0100 Subject: [PATCH 11/12] update french localization --- locale/fr/LC_MESSAGES/draw-on-your-screen.mo | Bin 14175 -> 14078 bytes locale/fr/LC_MESSAGES/draw-on-your-screen.po | 32 ++++++++----------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/locale/fr/LC_MESSAGES/draw-on-your-screen.mo b/locale/fr/LC_MESSAGES/draw-on-your-screen.mo index 012de3d54eca9c040d206401eb501effa1b42ee1..1ea4ddf1068d2f4e50d0d4b9ea23b4d7b3e2eacc 100644 GIT binary patch delta 4519 zcmZA232;@_9mnw#4G#i_gg{tBcnJvsAt4J`h?oRHLzR6K#Vwc@639kKik9U7bIyJDo_n_Y z@_FUA72aRtA~!jXQzU^D2b_zJaBlxyI_un}Sm!$6W$cJoFbe;PdH5guepEN-dQ;EC zID7>8a|`(BkIOI`8!;01_anl?29c(+wNObUt(yr6DFXpPsK#cMgH8Q zd}sjW$SrQIo!^SO{{Z&HX6%Y*F~)g}j6ysozC?|*V|V|8XuO+x0;XXh4#q0%jGN7! zs0I(9MtBsv;A!(SREJwo9sUNHWY>$619-kmq|giVP%|(aHAOW@pWG7rz8s8$T8inYfi2`i16ze^??u#ojp%90-lU)#{(yS$71SDENA>h;4B~B6 z1Bu+NDbB(^I1V*{Cy>FpI;*cm&CCvr!8edTy5p#sINy``SK$UHw6@)8B^wh^9h-vM zJmshn)T0_$k6P1RsHHfB8o*K104|`G;3Dd}E2s|NK<%jj7c&;ut2gtnDNW~u9Es}4 zeaN4CkPq&0PoWxGf@=7A9Ej_YKX;gqbUcGWyoKsW0@I@zOEO2GW~30cS7v(@)L=1Y zU^S}Y?WhNyKsE3QYAIUm{54cVt!98%M)i0z#mq79H>a6Tn_d+Kt=S69#|>6Li<+`8 zP*e5|sv}YCP}LJq_obtjWGre*XQQqwM_uQk_R@OP02+}$w}TI6)^kTG=)&`;2EV}R zcoVa55*tA`YNu+~uST|uJBSS4ePmw30n~3|3if5=Xle5CUYvv4Ya7il#_Ih)OF<8A zL2a^D)RNprtzi^*X_IwDjkFKy!5OHLPC(7nTvR(1R$qm>z5z8edr%!ehPv)NcH{Z( zs-3uH#xM-sFc5XaXw(Cyn)A$BR0me0W^jwyWZyTNXHeIDZhnPozeA8&)dTubPy^Yh z`Z(0Cf6S~wCd1XEdcFrWviDF8eQI7YZ=2nd{Pu>RI#7U%BAlCnn&}UdnSZ@@r<46n zat_B)|BKa=cokBq4@EldrXkDg7U57_hidp;)b2lnTCzVQO}NWuBpX8Qq@ni2B-HgK zDW3n{*KvZ`cgs-?Z$mb``xw)(1$AQ=TG9Cw)RN?*?wf|gu^RPL{0au~G-`m?Q8RV} z$79qWmK7&^6!IvnK{a?1wFG}cP5Hl259mrSwCU1O4HaS#AID+19QB$tp$2dmd6v6` zdTRoF=!b1Ms{K)@fp}9W1Sl*(^=J|50sE|e91E!5L{0s@L;Ux54tAtoi<+5bs19yI z`s@y(uD^h~?%${%v|Fg>bz+(*dM<{79yl7c=_aGruEbo6YG^;QobD8A1b;_u#z=Mf}@ zSD{9}!Q6-H=m)3=UpB8|C+c6K25=LDm`HC^dA`f1pnnD-{2newZKi!V7B3=yu78HV z#t)-rY!+$;7NDkn1!@4Bu`@QI?mvQKuoX4X5j3tDcnCf9w3LDxs76guy}8RgZeBFM zHoIl|*AFub&3UNTbvaJNm+>yVhI(+T8K1@aYl?HS{NL>97^J=gwR<;X5*|Y~i@Sts zpmVlA(!r?Bl#9B4ENV&SqXx7RgV=~A*o+03oZ~OaGdawE8Yjv*p{d!3YN!ddi$6iF zSu64axPBx2`gqKxUW&SICu$(SM|JorYA3482#!?@Sn(+eE5>4>z#0=CF&BYE_ zYF45~Sc_4(6t$UNz-U}&=eJ>3>aU^hKZ1HtGio!RF#m|^$VJn;PC*UcMm;ESkAGt{ zYS+i38tiKZQP&N!db-tfP#w%gJ#akUiBs+TOw@>A1zorm)#L4`2kb^Q zc);p!+xPFII&>1*NA3(p;8IdWbZ80Mk5?(IBw8LFQ+(WU9Ln?E8+PI_zDu-oOUYA2 zhdQ%?=yzMkVzQj9QN@lrRA>5G{Vn_%d6{e_zaeAEU~-VO9|L*5`w^K+w6pIg50IWj zzha9>`=MX4_G31M56CVOXD3%<3DIwe4$bECZ6)W*@WiP7ZunKR6`?;0H zn^u0s6bs0EeT~0WzQHC?qGzATo-Ci8k(f!r{5?exdFEzKVK(wZ6Yn z<^PUy>Z3^$i6=dX-kN8~Uh)|EHQ7h_v1{9KuVVq3NZOAWE4--n*U?B;klzyRUL9}y zxJPgnd7tPN(=pHIyU%r$J|dBJ<_PMo(xKmhQzW18v(UEL!5t&t*Ym5%Yb1qKkRq~* z=r~FqBI#r&(Xq{^?Q=4po5)&{Ob(HkNEXo#)YIg5gqOrkARm$wWFgVv{gjUf$&;#a zJWL)Xy~rN&F{vd{gdah-gmfp*k#1xLSw?EeB(j?fC-0C3vYE^!8_5f#nN*Sx8K(9B z2?ZVZku)+#6^_0>-+ks$+CgH;OgnprsagC5NhQT(4B1b9Ky(ZtLGmp5-{Xfa{EyiD zh>oGk+E7iXxS=2}p1jkew5W1^C|DbMwl-K)UN*n7f(yc}as30~yZc;->`_&|*v)*d zIutAlRu)x++^ovts&GN#r+3C?rDtZRXJ-a8vvM=<8Jd|DPD);LM}WI)PV5cuPMH!h xU{Y0aSxH%_CRqNrm0Q{x52^KFab4TRTY1cFoz2=@S{~2I&Q`SV@40`rPTj-r`TXyF?)~5Y z>qA?`Un<={$46{79IYgolt&rk_Zf5ac3m~*(>P-y@j7jrrVu(S~>Pg+IZVzBmLka1P#zYpt756>UfL zXg}VDN3Cz78u|{Zq5nh%)kJggPK?EFH~^D)zZpkCLoyF(gIQ>As6`EVJ*puMsGgic zHS{8mz^E?X(2qv`%nUv>WkFPZ%TUj)Lygo<)KncuS1*2#g68xRs-+*HgV#_M#PYO; zHWd?b5UK}-NaxG~d%hesG8<7tyaQ>YIe;38)7Hxv&3QyO#y^*WpW4*2eAHr@iRwWu zs)CiMx!i)9ie0E4>_>Ir3~CC_q3(MR)!@shHFOi{i;3nTjc6}xdUwWOEy?17D$L`9 zd(DHWiWZ_OUW&bOCGuw)`RI===-|hwhWHsCy(k_vav9d)sF5i|t(lpqdKbGCvM4-) zdchvli(63@yo;KWEB5+zR7Gu8KU+oT$<|EkFzW>CRBNfV1~ql=(-iKdu+?6;fEv21 zsG<7`)sQ&$rp|k#9vp<4n(?R}&qUoc9@<&+O$xBYX*wXJ+CoIaR>nxe5d9A~2zUxW1ss(}|ULHqv- z1@)i}wdgu?x8|k?YEIKp-)Ev0UjeG;6HyH*LG`>AHF9;R4!vN{52EfrjjHD&sw3Ai zf%lteny&|wQ4eNWN1+~=f_k7Fb>EZL&DIxD4LFSI**WVa`~5YmR;uocx2B=17Yw1G z7Zjo@2-x!))LgB%HX?&$j-eWQ5!JI#Q1wKlc=oVnTgRg6pMh#XH9qWPYEu}04f*G( z-o9)@Et)8{!f1>`ofl&o&O$n4o_t_023h##2I_x73{UBSdr;ROKuyIW)N_yF0Bk`0G{1%Vq4^9oLTxw( z6VixB@jIt56Nxj*5cT41sQurF>hTAtk^RDc--F@nqWzyrK~pdUZ^QdhLq7#0a4Bk6EVr&h z_568jGpeD#L%sN4)*Bej`F~Iyh@h1YW}v2M0{VHssiKgFkE0e*GZx@wM_^AX*EXGw8i^(7s>O8_RKXV1P`zS(%le_! zm+gI@Y|XP4Su3n-P}_7DPQ;UVJI3XBFYaj_p2PTSsHbs(oo1GygWFI;)PgDachsVZ z%JnM9#ydDKL@la0sQXJ%Q?nk`p*`r}>$m_vzp95VP6ep*MK}!WQ1_ihb>vf2gJTDKYa$07&I?fut-?60L5=)smx5aQENag7 z*)I;FhU_Rt;#uqOQ5F3WJK_7NMfM^3@e_OfON`;X<2~N)^I#No0N?s%Pl61A?71Dla z@vJ0;L_aF|WHjkQ_(e8LNc%Cgy@18!H)IcqCpw-c^9f5Kd}y?uwB?2PL*j1XgGC-b z*7ErSTh>r+ux0TpTYkYR%E&P?T_^T`n!*Yt5(S#(m-$$g=cx#eX+hquiS1S`t|K_?U}nRjwu lQ*qWiQBeiuA+FY+PfM!L80;h#1wv&(Zu$1z;|9O#`!D@0+Oz-w diff --git a/locale/fr/LC_MESSAGES/draw-on-your-screen.po b/locale/fr/LC_MESSAGES/draw-on-your-screen.po index ecf8142..ff4b1ba 100644 --- a/locale/fr/LC_MESSAGES/draw-on-your-screen.po +++ b/locale/fr/LC_MESSAGES/draw-on-your-screen.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: Draw On Your Screen 6.1\n" "Report-Msgid-Bugs-To: https://framagit.org/abakkk/DrawOnYourScreen/issues\n" -"POT-Creation-Date: 2020-09-19 15:32+0200\n" -"PO-Revision-Date: 2020-10-21 09:23+0200\n" +"POT-Creation-Date: 2021-02-17 14:14+0100\n" +"PO-Revision-Date: 2021-02-20 01:06+0100\n" "Last-Translator: Abakkk\n" "Language-Team: \n" "Language: fr\n" @@ -226,13 +226,15 @@ msgstr "Biseauté" msgid "%f px" msgstr "%f px" -#. Translators: text alignment -msgid "Right aligned" -msgstr "Aligné à droite" - msgid "Left aligned" msgstr "Aligné à gauche" +msgid "Centered" +msgstr "Centré" + +msgid "Right aligned" +msgstr "Aligné à droite" + msgctxt "drawing-tool" msgid "Free drawing" msgstr "Dessin libre" @@ -277,12 +279,6 @@ msgctxt "drawing-tool" msgid "Mirror" msgstr "Miroir" -msgid "Undo" -msgstr "Retirer" - -msgid "Redo" -msgstr "Rétablir" - msgid "Erase" msgstr "Effacer" @@ -601,8 +597,8 @@ msgstr "Ouvrir le dessin précédent" msgid "Add images from the clipboard" msgstr "Ajouter des images depuis le presse-papiers" -msgid "Redo last brushstroke" -msgstr "Rétablir le dernier tracé" +msgid "Redo" +msgstr "Rétablir" msgid "Save drawing" msgstr "Enregistrer le dessin" @@ -706,8 +702,8 @@ msgstr "Modifier la forme de l’extrémité des lignes" msgid "Change linejoin" msgstr "Modifier la jointure des segments de ligne" -msgid "Toggle text alignment" -msgstr "Alterner l’alignement du texte" +msgid "Change text alignment" +msgstr "Modifier l’alignement du texte" msgid "Add a drawing background" msgstr "Ajouter une couleur de fond au dessin" @@ -725,5 +721,5 @@ msgstr "Cacher le panneau et le dock" msgid "Square drawing area" msgstr "Rendre la zone de dessin carrée" -msgid "Undo last brushstroke" -msgstr "Retirer le dernier tracé" +msgid "Undo" +msgstr "Retirer" From 41e7a9d547c4eb7e2c25b35339c1bed8011641fa Mon Sep 17 00:00:00 2001 From: abakkk Date: Sat, 20 Feb 2021 01:14:22 +0100 Subject: [PATCH 12/12] version -> 11 --- NEWS | 11 +++++++++++ metadata.json | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index f009e94..5b82320 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,14 @@ +v11 - February 2021 +=================== + +* GS 40 compatibility (40.beta) +* Gtk4 port (preferences) +* Toggle animations (background, grid and square area) +* Multi-line text elements +* Start a new line with "Enter" (text tool) +* Add "centered" text alignment +* Convenient marks for rotation and stretch transformations + v10 - October 2020 ================== diff --git a/metadata.json b/metadata.json index 6c49c47..1272b75 100644 --- a/metadata.json +++ b/metadata.json @@ -16,7 +16,8 @@ "3.32", "3.34", "3.36", - "3.38" + "3.38", + "40" ], - "version": 10 + "version": 11 }