Merge branch 'dev' into 'master'
v6.3 Closes #42 See merge request abakkk/DrawOnYourScreen!14
14
NEWS
|
|
@ -1,3 +1,17 @@
|
|||
v6.3 - September 2020
|
||||
=====================
|
||||
|
||||
* Replace user stylesheet with proper drawing settings
|
||||
* Multi-palettes
|
||||
* Possibility to add insertable images from the clipboard
|
||||
* Image directory is configurable
|
||||
* Thumbnails in "Open drawing" sub-menu
|
||||
* Drawings can be directly inserted as an image.
|
||||
* Add a lot of icons in the menu and the OSD notifications
|
||||
* Group menu items at the bottom
|
||||
* Add tooltips to menu buttons
|
||||
* New "Persistent over toggles' setting #42
|
||||
|
||||
v6.2 - August 2020
|
||||
==================
|
||||
|
||||
|
|
|
|||
10
README.md
|
|
@ -7,7 +7,7 @@ Then save your beautiful work by taking a screenshot.
|
|||
|
||||
## Features
|
||||
|
||||
* Basic shapes (rectangle, circle, ellipse, line, curve, text, image, free)
|
||||
* Basic shapes (rectangle, circle, ellipse, line, curve, polygon, polyline, text, image, free)
|
||||
* Basic transformations (move, rotate, resize, stretch, mirror, inverse)
|
||||
* Smooth stroke
|
||||
* Draw over applications
|
||||
|
|
@ -26,7 +26,7 @@ Then save your beautiful work by taking a screenshot.
|
|||
6. `Super + Alt + D` to test
|
||||
7. [https://framagit.org/abakkk/DrawOnYourScreen/issues](https://framagit.org/abakkk/DrawOnYourScreen/issues) to say it doesn't work
|
||||
|
||||
## Details
|
||||
## Tips and tricks
|
||||
|
||||
* Draw arrows:
|
||||
|
||||
|
|
@ -42,7 +42,11 @@ Then save your beautiful work by taking a screenshot.
|
|||
|
||||
* Insertable images:
|
||||
|
||||
Add your images (jpeg, png, svg) to `~/.local/share/drawOnYourScreen/images/`.
|
||||
You can insert images (jpeg, png, svg) in your drawings. By default images are sought in `~/.local/share/drawOnYourScreen/images/` but the location is configurable in the preferences. Another way is to copy-past the images from Nautilus or any clipboard source by using the usual `Ctrl + V` shortcut inside the drawing mode.
|
||||
|
||||
* Eraser and SVG:
|
||||
|
||||
There is no eraser in SVG so when you export elements made with the eraser to a SVG file, they are colored with the background color, transparent if it is disabled. See `“Add a drawing background”` or edit the SVG file afterwards.
|
||||
|
||||
* Screenshot Tool extension:
|
||||
|
||||
|
|
|
|||
531
area.js
|
|
@ -1,4 +1,5 @@
|
|||
/* jslint esversion: 6 */
|
||||
/* exported Tools, DrawingArea */
|
||||
|
||||
/*
|
||||
* Copyright 2019 Abakkk
|
||||
|
|
@ -37,24 +38,41 @@ const Screenshot = imports.ui.screenshot;
|
|||
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = ExtensionUtils.getSettings ? ExtensionUtils : Me.imports.convenience;
|
||||
const Extension = Me.imports.extension;
|
||||
const Elements = Me.imports.elements;
|
||||
const Files = Me.imports.files;
|
||||
const Menu = Me.imports.menu;
|
||||
const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext;
|
||||
const pgettext = imports.gettext.domain(Me.metadata['gettext-domain']).pgettext;
|
||||
|
||||
const CAIRO_DEBUG_EXTENDS = false;
|
||||
const SVG_DEBUG_EXTENDS = false;
|
||||
const TEXT_CURSOR_TIME = 600; // ms
|
||||
const ELEMENT_GRABBER_TIME = 80; // ms, default is about 16 ms
|
||||
const GRID_TILES_HORIZONTAL_NUMBER = 30;
|
||||
|
||||
const { Shapes, ShapeNames, Transformations, LineCapNames, LineJoinNames, FillRuleNames,
|
||||
FontWeightNames, FontStyleNames, FontStretchNames, FontVariantNames } = Elements;
|
||||
const { Shapes, Transformations } = Elements;
|
||||
const { DisplayStrings } = Menu;
|
||||
|
||||
const FontGenericFamilies = ['Sans-Serif', 'Serif', 'Monospace', 'Cursive', 'Fantasy'];
|
||||
const Manipulations = { MOVE: 100, RESIZE: 101, MIRROR: 102 };
|
||||
const ManipulationNames = { 100: "Move", 101: "Resize", 102: "Mirror" };
|
||||
var Tools = Object.assign({}, Shapes, Manipulations);
|
||||
var ToolNames = Object.assign({}, ShapeNames, ManipulationNames);
|
||||
var Tools = Object.assign({
|
||||
getNameOf: function(value) {
|
||||
return Object.keys(this).find(key => this[key] == value);
|
||||
}
|
||||
}, Shapes, Manipulations);
|
||||
Object.defineProperty(Tools, 'getNameOf', { enumerable: false });
|
||||
|
||||
var FontGenericFamilies = ['Sans-Serif', 'Serif', 'Monospace', 'Cursive', 'Fantasy'];
|
||||
const getClutterColorFromString = function(string, fallback) {
|
||||
let [success, color] = Clutter.Color.from_string(string);
|
||||
color.toString = () => string;
|
||||
if (success)
|
||||
return color;
|
||||
|
||||
log(`${Me.metadata.uuid}: "${string}" color cannot be parsed.`);
|
||||
color = Clutter.Color.get_static(Clutter.StaticColor[fallback.toUpperCase()]);
|
||||
color.toString = () => fallback.slice(0, 1).toUpperCase() + fallback.slice(1);
|
||||
return color;
|
||||
};
|
||||
|
||||
// DrawingArea is the widget in which we draw, thanks to Cairo.
|
||||
// It creates and manages a DrawingElement for each "brushstroke".
|
||||
|
|
@ -62,18 +80,12 @@ var FontGenericFamilies = ['Sans-Serif', 'Serif', 'Monospace', 'Cursive', 'Fanta
|
|||
var DrawingArea = new Lang.Class({
|
||||
Name: 'DrawOnYourScreenDrawingArea',
|
||||
Extends: St.DrawingArea,
|
||||
Signals: { 'show-osd': { param_types: [GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_DOUBLE, GObject.TYPE_BOOLEAN] },
|
||||
'show-osd-gicon': { param_types: [Gio.Icon.$gtype, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_DOUBLE, GObject.TYPE_BOOLEAN] },
|
||||
Signals: { 'show-osd': { param_types: [Gio.Icon.$gtype, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_DOUBLE, GObject.TYPE_BOOLEAN] },
|
||||
'update-action-mode': {},
|
||||
'leave-drawing-mode': {} },
|
||||
|
||||
_init: function(params, monitor, helper, loadPersistent) {
|
||||
this.parent({ style_class: 'draw-on-your-screen', name: params.name});
|
||||
|
||||
this.connect('destroy', this._onDestroy.bind(this));
|
||||
this.reactiveHandler = this.connect('notify::reactive', this._onReactiveChanged.bind(this));
|
||||
|
||||
this.settings = Convenience.getSettings();
|
||||
this.monitor = monitor;
|
||||
this.helper = helper;
|
||||
|
||||
|
|
@ -81,16 +93,27 @@ var DrawingArea = new Lang.Class({
|
|||
this.undoneElements = [];
|
||||
this.currentElement = null;
|
||||
this.currentTool = Shapes.NONE;
|
||||
this.currentImage = 0;
|
||||
this.currentImage = null;
|
||||
this.currentTextRightAligned = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
|
||||
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);
|
||||
this.defaultFontFamily = this.currentFont.get_family();
|
||||
this.currentLineWidth = 5;
|
||||
this.currentLineJoin = Cairo.LineJoin.ROUND;
|
||||
this.currentLineCap = Cairo.LineCap.ROUND;
|
||||
this.currentFillRule = Cairo.FillRule.WINDING;
|
||||
this.isSquareArea = false;
|
||||
this.hasGrid = false;
|
||||
this.hasBackground = false;
|
||||
this.textHasCursor = false;
|
||||
this.dashedLine = false;
|
||||
this.fill = false;
|
||||
this.colors = [Clutter.Color.new(0, 0, 0, 255)];
|
||||
this.newThemeAttributes = {};
|
||||
this.oldThemeAttributes = {};
|
||||
|
||||
this.connect('destroy', this._onDestroy.bind(this));
|
||||
this.connect('notify::reactive', this._onReactiveChanged.bind(this));
|
||||
this.drawingSettingsChangedHandler = Me.drawingSettings.connect('changed', this._onDrawingSettingsChanged.bind(this));
|
||||
this._onDrawingSettingsChanged();
|
||||
|
||||
if (loadPersistent)
|
||||
this._loadPersistent();
|
||||
|
|
@ -98,7 +121,7 @@ var DrawingArea = new Lang.Class({
|
|||
|
||||
get menu() {
|
||||
if (!this._menu)
|
||||
this._menu = new Menu.DrawingMenu(this, this.monitor);
|
||||
this._menu = new Menu.DrawingMenu(this, this.monitor, Tools);
|
||||
return this._menu;
|
||||
},
|
||||
|
||||
|
|
@ -123,6 +146,52 @@ var DrawingArea = new Lang.Class({
|
|||
this._stopElementGrabber();
|
||||
},
|
||||
|
||||
get currentPalette() {
|
||||
return this._currentPalette;
|
||||
},
|
||||
|
||||
set currentPalette(palette) {
|
||||
this._currentPalette = palette;
|
||||
this.colors = palette[1].map(colorString => getClutterColorFromString(colorString, 'white'));
|
||||
if (!this.colors[0])
|
||||
this.colors.push(Clutter.Color.get_static(Clutter.StaticColor.WHITE));
|
||||
},
|
||||
|
||||
get currentImage() {
|
||||
if (!this._currentImage)
|
||||
this._currentImage = Files.Images.getNext(this._currentImage);
|
||||
|
||||
return this._currentImage;
|
||||
},
|
||||
|
||||
set currentImage(image) {
|
||||
this._currentImage = image;
|
||||
},
|
||||
|
||||
get currentFontFamily() {
|
||||
return this.currentFont.get_family();
|
||||
},
|
||||
|
||||
set currentFontFamily(family) {
|
||||
this.currentFont.set_family(family);
|
||||
},
|
||||
|
||||
get currentFontStyle() {
|
||||
return this.currentFont.get_style();
|
||||
},
|
||||
|
||||
set currentFontStyle(style) {
|
||||
this.currentFont.set_style(style);
|
||||
},
|
||||
|
||||
get currentFontWeight() {
|
||||
return this.currentFont.get_weight();
|
||||
},
|
||||
|
||||
set currentFontWeight(weight) {
|
||||
this.currentFont.set_weight(weight);
|
||||
},
|
||||
|
||||
get hasManipulationTool() {
|
||||
// No Object.values method in GS 3.24.
|
||||
return Object.keys(Manipulations).map(key => Manipulations[key]).indexOf(this.currentTool) != -1;
|
||||
|
|
@ -137,27 +206,12 @@ var DrawingArea = new Lang.Class({
|
|||
this.currentFillRule = evenodd ? Cairo.FillRule.EVEN_ODD : Cairo.FillRule.WINDING;
|
||||
},
|
||||
|
||||
getImages() {
|
||||
let images = Files.getImages();
|
||||
if (!images[this.currentImage])
|
||||
this.currentImage = Math.max(images.length - 1, 0);
|
||||
return images;
|
||||
},
|
||||
|
||||
get currentFontFamily() {
|
||||
return this._currentFontFamily || this.currentThemeFontFamily;
|
||||
},
|
||||
|
||||
set currentFontFamily(fontFamily) {
|
||||
this._currentFontFamily = fontFamily;
|
||||
},
|
||||
|
||||
get fontFamilies() {
|
||||
if (!this._fontFamilies) {
|
||||
let pangoFontFamilies = Elements.getPangoFontFamilies().filter(family => {
|
||||
return family != this.currentThemeFontFamily && FontGenericFamilies.indexOf(family) == -1;
|
||||
let otherFontFamilies = Elements.getAllFontFamilies().filter(family => {
|
||||
return family != this.defaultFontFamily && FontGenericFamilies.indexOf(family) == -1;
|
||||
});
|
||||
this._fontFamilies = [this.currentThemeFontFamily].concat(FontGenericFamilies, pangoFontFamilies);
|
||||
this._fontFamilies = [this.defaultFontFamily].concat(FontGenericFamilies, otherFontFamilies);
|
||||
}
|
||||
return this._fontFamilies;
|
||||
},
|
||||
|
|
@ -181,57 +235,44 @@ var DrawingArea = new Lang.Class({
|
|||
this.queue_repaint();
|
||||
},
|
||||
|
||||
_updateStyle: function() {
|
||||
try {
|
||||
let themeNode = this.get_theme_node();
|
||||
for (let i = 1; i < 10; i++) {
|
||||
this.colors[i] = themeNode.get_color('-drawing-color' + i);
|
||||
}
|
||||
let font = themeNode.get_font();
|
||||
this.newThemeAttributes.ThemeFontFamily = font.get_family();
|
||||
try { this.newThemeAttributes.FontWeight = font.get_weight(); } catch(e) { this.newThemeAttributes.FontWeight = Pango.Weight.NORMAL; }
|
||||
this.newThemeAttributes.FontStyle = font.get_style();
|
||||
this.newThemeAttributes.FontStretch = font.get_stretch();
|
||||
this.newThemeAttributes.FontVariant = font.get_variant();
|
||||
this.newThemeAttributes.TextRightAligned = themeNode.get_text_align() == St.TextAlign.RIGHT;
|
||||
this.newThemeAttributes.LineWidth = themeNode.get_length('-drawing-line-width');
|
||||
this.newThemeAttributes.LineJoin = themeNode.get_double('-drawing-line-join');
|
||||
this.newThemeAttributes.LineCap = themeNode.get_double('-drawing-line-cap');
|
||||
this.newThemeAttributes.FillRule = themeNode.get_double('-drawing-fill-rule');
|
||||
this.dashArray = [Math.abs(themeNode.get_length('-drawing-dash-array-on')), Math.abs(themeNode.get_length('-drawing-dash-array-off'))];
|
||||
this.dashOffset = themeNode.get_length('-drawing-dash-offset');
|
||||
this.gridGap = themeNode.get_length('-grid-overlay-gap');
|
||||
this.gridLineWidth = themeNode.get_length('-grid-overlay-line-width');
|
||||
this.gridInterlineWidth = themeNode.get_length('-grid-overlay-interline-width');
|
||||
this.gridColor = themeNode.get_color('-grid-overlay-color');
|
||||
this.squareAreaWidth = themeNode.get_length('-drawing-square-area-width');
|
||||
this.squareAreaHeight = themeNode.get_length('-drawing-square-area-height');
|
||||
this.activeBackgroundColor = themeNode.get_color('-drawing-background-color');
|
||||
} catch(e) {
|
||||
logError(e);
|
||||
_onDrawingSettingsChanged: function() {
|
||||
this.palettes = Me.drawingSettings.get_value('palettes').deep_unpack();
|
||||
if (!this.colors) {
|
||||
if (this.palettes[0])
|
||||
this.currentPalette = this.palettes[0];
|
||||
else
|
||||
this.currentPalette = ['Palette', ['White']];
|
||||
}
|
||||
if (!this.currentColor)
|
||||
this.currentColor = this.colors[0];
|
||||
|
||||
if (Me.drawingSettings.get_boolean('square-area-auto')) {
|
||||
this.squareAreaSize = Math.pow(2, 6);
|
||||
while (this.squareAreaSize * 2 < Math.min(this.monitor.width, this.monitor.height))
|
||||
this.squareAreaSize *= 2;
|
||||
} else {
|
||||
this.squareAreaSize = Me.drawingSettings.get_uint('square-area-size');
|
||||
}
|
||||
|
||||
for (let i = 1; i < 10; i++) {
|
||||
this.colors[i] = this.colors[i].alpha ? this.colors[i] : this.colors[0];
|
||||
this.areaBackgroundColor = getClutterColorFromString(Me.drawingSettings.get_string('background-color'), 'black');
|
||||
|
||||
this.gridColor = getClutterColorFromString(Me.drawingSettings.get_string('grid-color'), 'gray');
|
||||
if (Me.drawingSettings.get_boolean('grid-line-auto')) {
|
||||
this.gridLineSpacing = Math.round(this.monitor.width / (5 * GRID_TILES_HORIZONTAL_NUMBER));
|
||||
this.gridLineWidth = this.gridLineSpacing / 20;
|
||||
} else {
|
||||
this.gridLineSpacing = Me.drawingSettings.get_uint('grid-line-spacing');
|
||||
this.gridLineWidth = Math.round(Me.drawingSettings.get_double('grid-line-width') * 100) / 100;
|
||||
}
|
||||
this.currentColor = this.currentColor || this.colors[1];
|
||||
this._fontFamilies = null;
|
||||
// SVG does not support 'Ultra-heavy' weight (1000)
|
||||
this.newThemeAttributes.FontWeight = Math.min(this.newThemeAttributes.FontWeight, 900);
|
||||
this.newThemeAttributes.LineWidth = (this.newThemeAttributes.LineWidth > 0) ? this.newThemeAttributes.LineWidth : 3;
|
||||
this.newThemeAttributes.LineJoin = ([0, 1, 2].indexOf(this.newThemeAttributes.LineJoin) != -1) ? this.newThemeAttributes.LineJoin : Cairo.LineJoin.ROUND;
|
||||
this.newThemeAttributes.LineCap = ([0, 1, 2].indexOf(this.newThemeAttributes.LineCap) != -1) ? this.newThemeAttributes.LineCap : Cairo.LineCap.ROUND;
|
||||
this.newThemeAttributes.FillRule = ([0, 1].indexOf(this.newThemeAttributes.FillRule) != -1) ? this.newThemeAttributes.FillRule : Cairo.FillRule.WINDING;
|
||||
for (let attributeName in this.newThemeAttributes) {
|
||||
if (this.newThemeAttributes[attributeName] != this.oldThemeAttributes[attributeName]) {
|
||||
this.oldThemeAttributes[attributeName] = this.newThemeAttributes[attributeName];
|
||||
this[`current${attributeName}`] = this.newThemeAttributes[attributeName];
|
||||
}
|
||||
|
||||
this.dashOffset = Math.round(Me.drawingSettings.get_double('dash-offset') * 100) / 100;
|
||||
if (Me.drawingSettings.get_boolean('dash-array-auto')) {
|
||||
this.dashArray = [0, 0];
|
||||
} else {
|
||||
let on = Math.round(Me.drawingSettings.get_double('dash-array-on') * 100) / 100;
|
||||
let off = Math.round(Me.drawingSettings.get_double('dash-array-off') * 100) / 100;
|
||||
this.dashArray = [on, off];
|
||||
}
|
||||
this.gridGap = this.gridGap && this.gridGap >= 1 ? this.gridGap : 10;
|
||||
this.gridLineWidth = this.gridLineWidth || 0.4;
|
||||
this.gridInterlineWidth = this.gridInterlineWidth || 0.2;
|
||||
this.gridColor = this.gridColor && this.gridColor.alpha ? this.gridColor : Clutter.Color.new(127, 127, 127, 255);
|
||||
},
|
||||
|
||||
_repaint: function(cr) {
|
||||
|
|
@ -269,27 +310,27 @@ var DrawingArea = new Lang.Class({
|
|||
cr.restore();
|
||||
}
|
||||
|
||||
if (this.reactive && this.hasGrid && this.gridGap && this.gridGap >= 1) {
|
||||
if (this.reactive && this.hasGrid) {
|
||||
cr.save();
|
||||
Clutter.cairo_set_source_color(cr, this.gridColor);
|
||||
|
||||
let [gridX, gridY] = [0, 0];
|
||||
while (gridX < this.monitor.width / 2) {
|
||||
cr.setLineWidth((gridX / this.gridGap) % 5 ? this.gridInterlineWidth : this.gridLineWidth);
|
||||
cr.setLineWidth((gridX / this.gridLineSpacing) % 5 ? this.gridLineWidth / 2 : this.gridLineWidth);
|
||||
cr.moveTo(this.monitor.width / 2 + gridX, 0);
|
||||
cr.lineTo(this.monitor.width / 2 + gridX, this.monitor.height);
|
||||
cr.moveTo(this.monitor.width / 2 - gridX, 0);
|
||||
cr.lineTo(this.monitor.width / 2 - gridX, this.monitor.height);
|
||||
gridX += this.gridGap;
|
||||
gridX += this.gridLineSpacing;
|
||||
cr.stroke();
|
||||
}
|
||||
while (gridY < this.monitor.height / 2) {
|
||||
cr.setLineWidth((gridY / this.gridGap) % 5 ? this.gridInterlineWidth : this.gridLineWidth);
|
||||
cr.setLineWidth((gridY / this.gridLineSpacing) % 5 ? this.gridLineWidth / 2 : this.gridLineWidth);
|
||||
cr.moveTo(0, this.monitor.height / 2 + gridY);
|
||||
cr.lineTo(this.monitor.width, this.monitor.height / 2 + gridY);
|
||||
cr.moveTo(0, this.monitor.height / 2 - gridY);
|
||||
cr.lineTo(this.monitor.width, this.monitor.height / 2 - gridY);
|
||||
gridY += this.gridGap;
|
||||
gridY += this.gridLineSpacing;
|
||||
cr.stroke();
|
||||
}
|
||||
cr.restore();
|
||||
|
|
@ -362,8 +403,9 @@ var DrawingArea = new Lang.Class({
|
|||
event.get_key_symbol() == Clutter.KEY_KP_Enter ||
|
||||
event.get_key_symbol() == Clutter.KEY_Control_L) {
|
||||
if (this.currentElement.points.length == 2)
|
||||
this.emit('show-osd', null, _("Press <i>%s</i> to get\na fourth control point")
|
||||
.format(Gtk.accelerator_get_label(Clutter.KEY_Return, 0)), "", -1, true);
|
||||
// Translators: %s is a key label
|
||||
this.emit('show-osd', Files.Icons.ARC, _("Press <i>%s</i> to get\na fourth control point")
|
||||
.format(Gtk.accelerator_get_label(Clutter.KEY_Return, 0)), "", -1, true);
|
||||
this.currentElement.addPoint();
|
||||
this.updatePointerCursor(true);
|
||||
this._redisplay();
|
||||
|
|
@ -425,8 +467,9 @@ var DrawingArea = new Lang.Class({
|
|||
}
|
||||
|
||||
// Reduce computing without notable effect.
|
||||
if (Math.random() <= 0.75)
|
||||
if (event.get_time() - (this.elementGrabberTimestamp || 0) < ELEMENT_GRABBER_TIME)
|
||||
return;
|
||||
this.elementGrabberTimestamp = event.get_time();
|
||||
|
||||
let coords = event.get_coords();
|
||||
let [s, x, y] = this.transform_stage_point(coords[0], coords[1]);
|
||||
|
|
@ -459,7 +502,7 @@ var DrawingArea = new Lang.Class({
|
|||
if (this.grabbedElementLocked) {
|
||||
this.updatePointerCursor();
|
||||
let label = controlPressed ? _("Mark a point of symmetry") : _("Draw a line of symmetry");
|
||||
this.emit('show-osd', null, label, "", -1, true);
|
||||
this.emit('show-osd', Files.Icons.TOOL_MIRROR, label, "", -1, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -473,6 +516,10 @@ var DrawingArea = new Lang.Class({
|
|||
if (duplicate) {
|
||||
// deep cloning
|
||||
let copy = new this.grabbedElement.constructor(JSON.parse(JSON.stringify(this.grabbedElement)));
|
||||
if (this.grabbedElement.color)
|
||||
copy.color = this.grabbedElement.color;
|
||||
if (this.grabbedElement.font)
|
||||
copy.font = this.grabbedElement.font;
|
||||
if (this.grabbedElement.image)
|
||||
copy.image = this.grabbedElement.image;
|
||||
this.elements.push(copy);
|
||||
|
|
@ -560,34 +607,27 @@ var DrawingArea = new Lang.Class({
|
|||
if (this.currentTool == Shapes.TEXT) {
|
||||
this.currentElement = new Elements.DrawingElement({
|
||||
shape: this.currentTool,
|
||||
color: this.currentColor.to_string(),
|
||||
color: this.currentColor,
|
||||
eraser: eraser,
|
||||
font: {
|
||||
family: this.currentFontFamily,
|
||||
weight: this.currentFontWeight,
|
||||
style: this.currentFontStyle,
|
||||
stretch: this.currentFontStretch,
|
||||
variant: this.currentFontVariant },
|
||||
text: _("Text"),
|
||||
font: this.currentFont.copy(),
|
||||
// Translators: initial content of the text area
|
||||
text: pgettext("text-area-content", "Text"),
|
||||
textRightAligned: this.currentTextRightAligned,
|
||||
points: []
|
||||
});
|
||||
} else if (this.currentTool == Shapes.IMAGE) {
|
||||
let images = this.getImages();
|
||||
if (!images.length)
|
||||
return;
|
||||
this.currentElement = new Elements.DrawingElement({
|
||||
shape: this.currentTool,
|
||||
color: this.currentColor.to_string(),
|
||||
color: this.currentColor,
|
||||
eraser: eraser,
|
||||
image: images[this.currentImage],
|
||||
image: this.currentImage,
|
||||
operator: this.currentOperator,
|
||||
points: []
|
||||
});
|
||||
} else {
|
||||
this.currentElement = new Elements.DrawingElement({
|
||||
shape: this.currentTool,
|
||||
color: this.currentColor.to_string(),
|
||||
color: this.currentColor,
|
||||
eraser: eraser,
|
||||
fill: this.fill,
|
||||
fillRule: this.currentFillRule,
|
||||
|
|
@ -599,9 +639,12 @@ var DrawingArea = new Lang.Class({
|
|||
|
||||
this.currentElement.startDrawing(startX, startY);
|
||||
|
||||
if (this.currentTool == Shapes.POLYGON || this.currentTool == Shapes.POLYLINE)
|
||||
this.emit('show-osd', null, _("Press <i>%s</i> to mark vertices")
|
||||
if (this.currentTool == Shapes.POLYGON || this.currentTool == Shapes.POLYLINE) {
|
||||
let icon = Files.Icons[this.currentTool == Shapes.POLYGON ? 'TOOL_POLYGON' : 'TOOL_POLYLINE'];
|
||||
// Translators: %s is a key label
|
||||
this.emit('show-osd', icon, _("Press <i>%s</i> to mark vertices")
|
||||
.format(Gtk.accelerator_get_label(Clutter.KEY_Return, 0)), "", -1, true);
|
||||
}
|
||||
|
||||
this.motionHandler = this.connect('motion-event', (actor, event) => {
|
||||
if (this.spaceKeyPressed)
|
||||
|
|
@ -661,8 +704,9 @@ var DrawingArea = new Lang.Class({
|
|||
let [x, y] = [this.currentElement.x, this.currentElement.y];
|
||||
this.currentElement.text = '';
|
||||
this.currentElement.cursorPosition = 0;
|
||||
this.emit('show-osd', null, _("Type your text and press <i>%s</i>")
|
||||
.format(Gtk.accelerator_get_label(Clutter.KEY_Escape, 0)), "", -1, true);
|
||||
// Translators: %s is a key label
|
||||
this.emit('show-osd', Files.Icons.TOOL_TEXT, _("Type your text and press <i>%s</i>")
|
||||
.format(Gtk.accelerator_get_label(Clutter.KEY_Escape, 0)), "", -1, true);
|
||||
this._updateTextCursorTimeout();
|
||||
this.textHasCursor = true;
|
||||
this._redisplay();
|
||||
|
|
@ -749,7 +793,7 @@ var DrawingArea = new Lang.Class({
|
|||
setPointerCursor: function(pointerCursorName) {
|
||||
if (!this.currentPointerCursorName || this.currentPointerCursorName != pointerCursorName) {
|
||||
this.currentPointerCursorName = pointerCursorName;
|
||||
Extension.setCursor(pointerCursorName);
|
||||
Me.stateObj.areaManager.setCursor(pointerCursorName);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -835,7 +879,7 @@ var DrawingArea = new Lang.Class({
|
|||
|
||||
toggleBackground: function() {
|
||||
this.hasBackground = !this.hasBackground;
|
||||
this.get_parent().set_background_color(this.hasBackground ? this.activeBackgroundColor : null);
|
||||
this.get_parent().set_background_color(this.hasBackground ? this.areaBackgroundColor : null);
|
||||
},
|
||||
|
||||
toggleGrid: function() {
|
||||
|
|
@ -846,10 +890,8 @@ var DrawingArea = new Lang.Class({
|
|||
toggleSquareArea: function() {
|
||||
this.isSquareArea = !this.isSquareArea;
|
||||
if (this.isSquareArea) {
|
||||
let width = this.squareAreaWidth || this.squareAreaHeight || Math.min(this.monitor.width, this.monitor.height) * 3 / 4;
|
||||
let height = this.squareAreaHeight || this.squareAreaWidth || Math.min(this.monitor.width, this.monitor.height) * 3 / 4;
|
||||
this.set_position(Math.floor(this.monitor.width / 2 - width / 2), Math.floor(this.monitor.height / 2 - height / 2));
|
||||
this.set_size(width, height);
|
||||
this.set_position((this.monitor.width - this.squareAreaSize) / 2, (this.monitor.height - this.squareAreaSize) / 2);
|
||||
this.set_size(this.squareAreaSize, this.squareAreaSize);
|
||||
this.add_style_class_name('draw-on-your-screen-square-area');
|
||||
} else {
|
||||
this.set_position(0, 0);
|
||||
|
|
@ -858,76 +900,87 @@ var DrawingArea = new Lang.Class({
|
|||
}
|
||||
},
|
||||
|
||||
switchColor: function() {
|
||||
this.selectColor((this.currentColor == this.colors[1]) ? 2 : 1);
|
||||
},
|
||||
|
||||
selectColor: function(index) {
|
||||
if (!this.colors[index])
|
||||
return;
|
||||
|
||||
this.currentColor = this.colors[index];
|
||||
if (this.currentElement) {
|
||||
this.currentElement.color = this.currentColor.to_string();
|
||||
this.currentElement.color = this.currentColor;
|
||||
this._redisplay();
|
||||
}
|
||||
// Foreground color markup is not displayed since 3.36, use style instead but the transparency is lost.
|
||||
this.emit('show-osd', null, this.currentColor.to_string(), this.currentColor.to_string().slice(0, 7), -1, false);
|
||||
this.emit('show-osd', Files.Icons.COLOR, String(this.currentColor), this.currentColor.to_string().slice(0, 7), -1, false);
|
||||
},
|
||||
|
||||
selectTool: function(tool) {
|
||||
this.currentTool = tool;
|
||||
this.emit('show-osd', null, _(ToolNames[tool]), "", -1, false);
|
||||
this.emit('show-osd', Files.Icons[`TOOL_${Tools.getNameOf(tool)}`] || null, DisplayStrings.Tool[tool], "", -1, false);
|
||||
this.updatePointerCursor();
|
||||
},
|
||||
|
||||
switchFill: function() {
|
||||
this.fill = !this.fill;
|
||||
this.emit('show-osd', null, this.fill ? _("Fill") : _("Outline"), "", -1, false);
|
||||
},
|
||||
|
||||
switchDash: function() {
|
||||
this.dashedLine = !this.dashedLine;
|
||||
this.emit('show-osd', null, this.dashedLine ? _("Dashed line") : _("Full line"), "", -1, false);
|
||||
},
|
||||
|
||||
incrementLineWidth: function(increment) {
|
||||
this.currentLineWidth = Math.max(this.currentLineWidth + increment, 0);
|
||||
this.emit('show-osd', null, _("%d px").format(this.currentLineWidth), "", 2 * this.currentLineWidth, false);
|
||||
},
|
||||
|
||||
switchLineJoin: function() {
|
||||
this.currentLineJoin = this.currentLineJoin == 2 ? 0 : this.currentLineJoin + 1;
|
||||
this.emit('show-osd', null, _(LineJoinNames[this.currentLineJoin]), "", -1, false);
|
||||
},
|
||||
|
||||
switchLineCap: function() {
|
||||
this.currentLineCap = this.currentLineCap == 2 ? 0 : this.currentLineCap + 1;
|
||||
this.emit('show-osd', null, _(LineCapNames[this.currentLineCap]), "", -1, false);
|
||||
let icon = Files.Icons[this.fill ? 'FILL' : 'STROKE'];
|
||||
this.emit('show-osd', icon, DisplayStrings.getFill(this.fill), "", -1, false);
|
||||
},
|
||||
|
||||
switchFillRule: function() {
|
||||
this.currentFillRule = this.currentFillRule == 1 ? 0 : this.currentFillRule + 1;
|
||||
this.emit('show-osd', null, _(FillRuleNames[this.currentFillRule]), "", -1, false);
|
||||
let icon = Files.Icons[this.currentEvenodd ? 'FILLRULE_EVENODD' : 'FILLRULE_NONZERO'];
|
||||
this.emit('show-osd', icon, DisplayStrings.FillRule[this.currentFillRule], "", -1, false);
|
||||
},
|
||||
|
||||
switchColorPalette: function(reverse) {
|
||||
let index = this.palettes.indexOf(this.currentPalette);
|
||||
if (reverse)
|
||||
this.currentPalette = index <= 0 ? this.palettes[this.palettes.length - 1] : this.palettes[index - 1];
|
||||
else
|
||||
this.currentPalette = index == this.palettes.length - 1 ? this.palettes[0] : this.palettes[index + 1];
|
||||
this.emit('show-osd', Files.Icons.PALETTE, this.currentPalette[0], "", -1, false);
|
||||
},
|
||||
|
||||
switchDash: function() {
|
||||
this.dashedLine = !this.dashedLine;
|
||||
let icon = Files.Icons[this.dashedLine ? 'DASHED_LINE' : 'FULL_LINE'];
|
||||
this.emit('show-osd', icon, DisplayStrings.getDashedLine(this.dashedLine), "", -1, false);
|
||||
},
|
||||
|
||||
incrementLineWidth: function(increment) {
|
||||
this.currentLineWidth = Math.max(this.currentLineWidth + increment, 0);
|
||||
this.emit('show-osd', null, DisplayStrings.getPixels(this.currentLineWidth), "", 2 * this.currentLineWidth, false);
|
||||
},
|
||||
|
||||
switchLineJoin: function() {
|
||||
this.currentLineJoin = this.currentLineJoin == 2 ? 0 : this.currentLineJoin + 1;
|
||||
this.emit('show-osd', Files.Icons.LINEJOIN, DisplayStrings.LineJoin[this.currentLineJoin], "", -1, false);
|
||||
},
|
||||
|
||||
switchLineCap: function() {
|
||||
this.currentLineCap = this.currentLineCap == 2 ? 0 : this.currentLineCap + 1;
|
||||
this.emit('show-osd', Files.Icons.LINECAP, DisplayStrings.LineCap[this.currentLineCap], "", -1, false);
|
||||
},
|
||||
|
||||
switchFontWeight: function() {
|
||||
let fontWeights = Object.keys(FontWeightNames).map(key => Number(key));
|
||||
let fontWeights = Object.keys(DisplayStrings.FontWeight).map(key => Number(key));
|
||||
let index = fontWeights.indexOf(this.currentFontWeight);
|
||||
this.currentFontWeight = index == fontWeights.length - 1 ? fontWeights[0] : fontWeights[index + 1];
|
||||
if (this.currentElement && this.currentElement.font) {
|
||||
this.currentElement.font.weight = this.currentFontWeight;
|
||||
this.currentElement.font.set_weight(this.currentFontWeight);
|
||||
this._redisplay();
|
||||
}
|
||||
this.emit('show-osd', null, `<span font_weight="${this.currentFontWeight}">` +
|
||||
`${_(FontWeightNames[this.currentFontWeight])}</span>`, "", -1, false);
|
||||
this.emit('show-osd', Files.Icons.FONT_WEIGHT, `<span font_weight="${this.currentFontWeight}">` +
|
||||
`${DisplayStrings.FontWeight[this.currentFontWeight]}</span>`, "", -1, false);
|
||||
},
|
||||
|
||||
switchFontStyle: function() {
|
||||
this.currentFontStyle = this.currentFontStyle == 2 ? 0 : this.currentFontStyle + 1;
|
||||
if (this.currentElement && this.currentElement.font) {
|
||||
this.currentElement.font.style = this.currentFontStyle;
|
||||
this.currentElement.font.set_style(this.currentFontStyle);
|
||||
this._redisplay();
|
||||
}
|
||||
this.emit('show-osd', null, `<span font_style="${FontStyleNames[this.currentFontStyle].toLowerCase()}">` +
|
||||
`${_(FontStyleNames[this.currentFontStyle])}</span>`, "", -1, false);
|
||||
this.emit('show-osd', Files.Icons.FONT_STYLE, `<span font_style="${DisplayStrings.FontStyleMarkup[this.currentFontStyle]}">` +
|
||||
`${DisplayStrings.FontStyle[this.currentFontStyle]}</span>`, "", -1, false);
|
||||
},
|
||||
|
||||
switchFontFamily: function(reverse) {
|
||||
|
|
@ -937,10 +990,10 @@ var DrawingArea = new Lang.Class({
|
|||
else
|
||||
this.currentFontFamily = (index == this.fontFamilies.length - 1) ? this.fontFamilies[0] : this.fontFamilies[index + 1];
|
||||
if (this.currentElement && this.currentElement.font) {
|
||||
this.currentElement.font.family = this.currentFontFamily;
|
||||
this.currentElement.font.set_family(this.currentFontFamily);
|
||||
this._redisplay();
|
||||
}
|
||||
this.emit('show-osd', null, `<span font_family="${this.currentFontFamily}">${_(this.currentFontFamily)}</span>`, "", -1, false);
|
||||
this.emit('show-osd', Files.Icons.FONT_FAMILY, `<span font_family="${this.currentFontFamily}">${DisplayStrings.getFontFamily(this.currentFontFamily)}</span>`, "", -1, false);
|
||||
},
|
||||
|
||||
switchTextAlignment: function() {
|
||||
|
|
@ -949,16 +1002,23 @@ var DrawingArea = new Lang.Class({
|
|||
this.currentElement.textRightAligned = this.currentTextRightAligned;
|
||||
this._redisplay();
|
||||
}
|
||||
this.emit('show-osd', null, this.currentTextRightAligned ? _("Right aligned") : _("Left aligned"), "", -1, false);
|
||||
let icon = Files.Icons[this.currentTextRightAligned ? 'RIGHT_ALIGNED' : 'LEFT_ALIGNED'];
|
||||
this.emit('show-osd', icon, DisplayStrings.getTextAlignment(this.currentTextRightAligned), "", -1, false);
|
||||
},
|
||||
|
||||
switchImageFile: function() {
|
||||
let images = this.getImages();
|
||||
if (!images.length)
|
||||
return;
|
||||
if (images.length > 1)
|
||||
this.currentImage = this.currentImage == images.length - 1 ? 0 : this.currentImage + 1;
|
||||
this.emit('show-osd-gicon', images[this.currentImage].gicon, images[this.currentImage].toString(), "", -1, false);
|
||||
switchImageFile: function(reverse) {
|
||||
this.currentImage = Files.Images[reverse ? 'getPrevious' : 'getNext'](this.currentImage);
|
||||
if (this.currentImage)
|
||||
this.emit('show-osd', this.currentImage.gicon, this.currentImage.toString(), "", -1, false);
|
||||
},
|
||||
|
||||
pasteImageFiles: function() {
|
||||
Files.Images.addImagesFromClipboard(lastImage => {
|
||||
this.currentImage = lastImage;
|
||||
this.currentTool = Shapes.IMAGE;
|
||||
this.updatePointerCursor();
|
||||
this.emit('show-osd', this.currentImage.gicon, this.currentImage.toString(), "", -1, false);
|
||||
});
|
||||
},
|
||||
|
||||
toggleHelp: function() {
|
||||
|
|
@ -984,7 +1044,7 @@ var DrawingArea = new Lang.Class({
|
|||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
this.disconnect(this.reactiveHandler);
|
||||
Me.drawingSettings.disconnect(this.drawingSettingsChangedHandler);
|
||||
this.erase();
|
||||
if (this._menu)
|
||||
this._menu.disable();
|
||||
|
|
@ -1001,11 +1061,10 @@ var DrawingArea = new Lang.Class({
|
|||
this.buttonPressedHandler = this.connect('button-press-event', this._onButtonPressed.bind(this));
|
||||
this.keyboardPopupMenuHandler = this.connect('popup-menu', this._onKeyboardPopupMenu.bind(this));
|
||||
this.scrollHandler = this.connect('scroll-event', this._onScroll.bind(this));
|
||||
this.get_parent().set_background_color(this.reactive && this.hasBackground ? this.activeBackgroundColor : null);
|
||||
this._updateStyle();
|
||||
this.get_parent().set_background_color(this.reactive && this.hasBackground ? this.areaBackgroundColor : null);
|
||||
},
|
||||
|
||||
leaveDrawingMode: function(save) {
|
||||
leaveDrawingMode: function(save, erase) {
|
||||
if (this.stageKeyPressedHandler) {
|
||||
global.stage.disconnect(this.stageKeyPressedHandler);
|
||||
this.stageKeyPressedHandler = null;
|
||||
|
|
@ -1041,14 +1100,49 @@ var DrawingArea = new Lang.Class({
|
|||
|
||||
this.currentElement = null;
|
||||
this._stopTextCursorTimeout();
|
||||
this._redisplay();
|
||||
if (erase)
|
||||
this.erase();
|
||||
else
|
||||
this._redisplay();
|
||||
this.closeMenu();
|
||||
this.get_parent().set_background_color(null);
|
||||
Files.Images.reset();
|
||||
if (save)
|
||||
this.savePersistent();
|
||||
},
|
||||
|
||||
saveAsSvg: function() {
|
||||
// Used by the menu.
|
||||
getSvgContentsForJson(json) {
|
||||
let elements = [];
|
||||
let elementsContent = '';
|
||||
|
||||
elements.push(...JSON.parse(json.contents).map(object => {
|
||||
if (object.color)
|
||||
object.color = getClutterColorFromString(object.color, 'white');
|
||||
if (object.font && typeof object.font == 'string')
|
||||
object.font = Pango.FontDescription.from_string(object.font);
|
||||
if (object.image)
|
||||
object.image = new Files.Image(object.image);
|
||||
return new Elements.DrawingElement(object);
|
||||
}));
|
||||
elements.forEach(element => elementsContent += element.buildSVG('transparent'));
|
||||
|
||||
let prefixes = 'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"';
|
||||
|
||||
let getGiconSvgContent = () => {
|
||||
let size = Math.min(this.monitor.width, this.monitor.height);
|
||||
let [x, y] = [(this.monitor.width - size) / 2, (this.monitor.height - size) / 2];
|
||||
return `<svg viewBox="${x} ${y} ${size} ${size}" ${prefixes}>${elementsContent}\n</svg>`;
|
||||
};
|
||||
|
||||
let getImageSvgContent = () => {
|
||||
return `<svg viewBox="0 0 ${this.width} ${this.height}" ${prefixes}>${elementsContent}\n</svg>`;
|
||||
};
|
||||
|
||||
return [getGiconSvgContent, getImageSvgContent];
|
||||
},
|
||||
|
||||
exportToSvg: function() {
|
||||
// stop drawing or writing
|
||||
if (this.currentElement && this.currentElement.shape == Shapes.TEXT && this.isWriting) {
|
||||
this._stopWriting();
|
||||
|
|
@ -1062,7 +1156,7 @@ var DrawingArea = new Lang.Class({
|
|||
let content = `<svg viewBox="0 0 ${this.width} ${this.height}" ${prefixes}>`;
|
||||
if (SVG_DEBUG_EXTENDS)
|
||||
content = `<svg viewBox="${-this.width} ${-this.height} ${2 * this.width} ${2 * this.height}" xmlns="http://www.w3.org/2000/svg">`;
|
||||
let backgroundColorString = this.hasBackground ? this.activeBackgroundColor.to_string() : 'transparent';
|
||||
let backgroundColorString = this.hasBackground ? String(this.areaBackgroundColor) : 'transparent';
|
||||
if (backgroundColorString != 'transparent') {
|
||||
content += `\n <rect id="background" width="100%" height="100%" fill="${backgroundColorString}"/>`;
|
||||
}
|
||||
|
|
@ -1070,19 +1164,10 @@ var DrawingArea = new Lang.Class({
|
|||
content += `\n <line stroke="black" x1="0" y1="${-this.height}" x2="0" y2="${this.height}"/>`;
|
||||
content += `\n <line stroke="black" x1="${-this.width}" y1="0" x2="${this.width}" y2="0"/>`;
|
||||
}
|
||||
for (let i = 0; i < this.elements.length; i++) {
|
||||
content += this.elements[i].buildSVG(backgroundColorString);
|
||||
}
|
||||
this.elements.forEach(element => content += element.buildSVG(backgroundColorString));
|
||||
content += "\n</svg>";
|
||||
|
||||
let filename = `${Me.metadata['svg-file-name']} ${Files.getDateString()}.svg`;
|
||||
let dir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES);
|
||||
let path = GLib.build_filenamev([dir, filename]);
|
||||
if (GLib.file_test(path, GLib.FileTest.EXISTS))
|
||||
return false;
|
||||
let success = GLib.file_set_contents(path, content);
|
||||
|
||||
if (success) {
|
||||
if (Files.saveSvg(content)) {
|
||||
// pass the parent (bgContainer) to Flashspot because coords of this are relative
|
||||
let flashspot = new Screenshot.Flashspot(this.get_parent());
|
||||
flashspot.fire();
|
||||
|
|
@ -1095,7 +1180,7 @@ var DrawingArea = new Lang.Class({
|
|||
}
|
||||
},
|
||||
|
||||
_saveAsJson: function(name, notify, callback) {
|
||||
_saveAsJson: function(json, notify, callback) {
|
||||
// stop drawing or writing
|
||||
if (this.currentElement && this.currentElement.shape == Shapes.TEXT && this.isWriting) {
|
||||
this._stopWriting();
|
||||
|
|
@ -1103,46 +1188,31 @@ var DrawingArea = new Lang.Class({
|
|||
this._stopDrawing();
|
||||
}
|
||||
|
||||
let json = new Files.Json({ name });
|
||||
let oldContents;
|
||||
|
||||
if (name == Me.metadata['persistent-file-name']) {
|
||||
let oldContents = json.contents;
|
||||
// do not create a file to write just an empty array
|
||||
if (!oldContents && this.elements.length == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// do not use "content = JSON.stringify(this.elements, null, 2);", neither "content = JSON.stringify(this.elements);"
|
||||
// do compromise between disk usage and human readability
|
||||
let contents = `[\n ` + new Array(...this.elements.map(element => JSON.stringify(element))).join(`,\n\n `) + `\n]`;
|
||||
|
||||
if (name == Me.metadata['persistent-file-name'] && contents == oldContents)
|
||||
return;
|
||||
let contents = this.elements.length ? `[\n ` + new Array(...this.elements.map(element => JSON.stringify(element))).join(`,\n\n `) + `\n]` : '[]';
|
||||
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||
json.contents = contents;
|
||||
if (notify)
|
||||
this.emit('show-osd', 'document-save-symbolic', name, "", -1, false);
|
||||
if (name != Me.metadata['persistent-file-name']) {
|
||||
this.jsonName = name;
|
||||
this.lastJsonContents = contents;
|
||||
}
|
||||
this.emit('show-osd', Files.Icons.SAVE, json.name, "", -1, false);
|
||||
if (!json.isPersistent)
|
||||
this.currentJson = json;
|
||||
if (callback)
|
||||
callback();
|
||||
});
|
||||
},
|
||||
|
||||
saveAsJsonWithName: function(name, callback) {
|
||||
this._saveAsJson(name, false, callback);
|
||||
this._saveAsJson(Files.Jsons.getNamed(name), false, callback);
|
||||
},
|
||||
|
||||
saveAsJson: function() {
|
||||
this._saveAsJson(Files.getDateString(), true);
|
||||
saveAsJson: function(notify, callback) {
|
||||
this._saveAsJson(Files.Jsons.getDated(), notify, callback);
|
||||
},
|
||||
|
||||
savePersistent: function() {
|
||||
this._saveAsJson(Me.metadata['persistent-file-name']);
|
||||
this._saveAsJson(Files.Jsons.getPersistent());
|
||||
},
|
||||
|
||||
syncPersistent: function() {
|
||||
|
|
@ -1154,7 +1224,7 @@ var DrawingArea = new Lang.Class({
|
|||
|
||||
},
|
||||
|
||||
_loadJson: function(name, notify) {
|
||||
_loadJson: function(json, notify) {
|
||||
// stop drawing or writing
|
||||
if (this.currentElement && this.currentElement.shape == Shapes.TEXT && this.isWriting) {
|
||||
this._stopWriting();
|
||||
|
|
@ -1164,56 +1234,49 @@ var DrawingArea = new Lang.Class({
|
|||
this.elements = [];
|
||||
this.currentElement = null;
|
||||
|
||||
let contents = (new Files.Json({ name })).contents;
|
||||
if (!contents)
|
||||
if (!json.contents)
|
||||
return;
|
||||
|
||||
this.elements.push(...JSON.parse(contents).map(object => {
|
||||
this.elements.push(...JSON.parse(json.contents).map(object => {
|
||||
if (object.color)
|
||||
object.color = getClutterColorFromString(object.color, 'white');
|
||||
if (object.font && typeof object.font == 'string')
|
||||
object.font = Pango.FontDescription.from_string(object.font);
|
||||
if (object.image)
|
||||
object.image = new Files.Image(object.image);
|
||||
return new Elements.DrawingElement(object);
|
||||
}));
|
||||
|
||||
if (notify)
|
||||
this.emit('show-osd', 'document-open-symbolic', name, "", -1, false);
|
||||
if (name != Me.metadata['persistent-file-name']) {
|
||||
this.jsonName = name;
|
||||
this.lastJsonContents = contents;
|
||||
}
|
||||
this.emit('show-osd', Files.Icons.OPEN, json.name, "", -1, false);
|
||||
if (!json.isPersistent)
|
||||
this.currentJson = json;
|
||||
},
|
||||
|
||||
_loadPersistent: function() {
|
||||
this._loadJson(Me.metadata['persistent-file-name']);
|
||||
this._loadJson(Files.Jsons.getPersistent());
|
||||
},
|
||||
|
||||
loadJson: function(name, notify) {
|
||||
this._loadJson(name, notify);
|
||||
loadJson: function(json, notify) {
|
||||
this._loadJson(json, notify);
|
||||
this._redisplay();
|
||||
},
|
||||
|
||||
loadNextJson: function() {
|
||||
let names = Files.getJsons().map(json => json.name);
|
||||
|
||||
if (!names.length)
|
||||
return;
|
||||
|
||||
let nextName = names[this.jsonName && names.indexOf(this.jsonName) != names.length - 1 ? names.indexOf(this.jsonName) + 1 : 0];
|
||||
this.loadJson(nextName, true);
|
||||
loadPreviousJson: function() {
|
||||
let json = Files.Jsons.getPrevious(this.currentJson || null);
|
||||
if (json)
|
||||
this.loadJson(json, true);
|
||||
},
|
||||
|
||||
loadPreviousJson: function() {
|
||||
let names = Files.getJsons().map(json => json.name);
|
||||
|
||||
if (!names.length)
|
||||
return;
|
||||
|
||||
let previousName = names[this.jsonName && names.indexOf(this.jsonName) > 0 ? names.indexOf(this.jsonName) - 1 : names.length - 1];
|
||||
this.loadJson(previousName, true);
|
||||
loadNextJson: function() {
|
||||
let json = Files.Jsons.getNext(this.currentJson || null);
|
||||
if (json)
|
||||
this.loadJson(json, true);
|
||||
},
|
||||
|
||||
get drawingContentsHasChanged() {
|
||||
let contents = `[\n ` + new Array(...this.elements.map(element => JSON.stringify(element))).join(`,\n\n `) + `\n]`;
|
||||
return contents != this.lastJsonContents;
|
||||
return contents != (this.currentJson && this.currentJson.contents);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
151
data/default.css
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* WARNING : user.css may be obsolete after an extension update.
|
||||
*
|
||||
* ~/.local/share/drawOnYourScreen/user.css file is automatically generated by activating "Edit style".
|
||||
* Delete ~/.local/share/drawOnYourScreen/user.css file to retrieve the default drawing style.
|
||||
*
|
||||
* Except for the font, you don't need to restart the extension.
|
||||
* Just save this file as ~/.local/share/drawOnYourScreen/user.css and the changes will be applied for your next brushstroke.
|
||||
* Some attributes are modifiable in the user interface.
|
||||
*
|
||||
* line-join (no string):
|
||||
* 0 : miter, 1 : round, 2 : bevel
|
||||
* line-cap (no string):
|
||||
* 0 : butt, 1 : round, 2 : square
|
||||
* fill-rule (no string):
|
||||
* 0 : nonzero (winding in Cairo), 1 : evenodd
|
||||
*
|
||||
* dash:
|
||||
* By default, it is computed from the line width.
|
||||
* dash-array-on is the length of dashes (put 0.1 to get dots or squares according to line-cap).
|
||||
* dash-array-off is the length of gaps.
|
||||
*
|
||||
* square area:
|
||||
* Drawing in a square area is convenient when using the extension as a vector graphics editor. By default,
|
||||
* when toggling 'Square drawing area', the area is sized to 75% of monitor size. You can fix and customize this size
|
||||
* by uncommenting square-area-width and square-area-height lines.
|
||||
*
|
||||
* font:
|
||||
* Only one family : no comma separated list of families like "font1, font2, ..., Sans-Serif".
|
||||
* Font family can be any font installed, or a generic family name (Serif, Sans-Serif, Monospace, Cursive, Fantasy).
|
||||
* Font weight and font style : no upper case when string.
|
||||
*
|
||||
* text-align: left or right.
|
||||
*
|
||||
*/
|
||||
|
||||
.draw-on-your-screen {
|
||||
-drawing-line-width: 5px;
|
||||
-drawing-line-join: 1;
|
||||
-drawing-line-cap: 1;
|
||||
-drawing-fill-rule: 0;
|
||||
/*-drawing-dash-array-on: 5px;*/
|
||||
/*-drawing-dash-array-off: 15px;*/
|
||||
/*-drawing-dash-offset: 0px;*/
|
||||
-drawing-background-color: #2e2e2e;
|
||||
-grid-overlay-gap: 10px;
|
||||
-grid-overlay-line-width: 0.4px;
|
||||
-grid-overlay-interline-width: 0.2px;
|
||||
-grid-overlay-color: Gray;
|
||||
/*-drawing-square-area-width: 512px;*/
|
||||
/*-drawing-square-area-height: 512px;*/
|
||||
font-family: Cantarell;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Palette */
|
||||
.draw-on-your-screen {
|
||||
-drawing-color1: HotPink;
|
||||
-drawing-color2: Cyan;
|
||||
-drawing-color3: yellow;
|
||||
-drawing-color4: Orangered;
|
||||
-drawing-color5: Chartreuse;
|
||||
-drawing-color6: DarkViolet;
|
||||
-drawing-color7: White;
|
||||
-drawing-color8: Gray;
|
||||
-drawing-color9: Black;
|
||||
}
|
||||
|
||||
/*
|
||||
Example of alternative palettes from GNOME HIG Colors.
|
||||
https://developer.gnome.org/hig/stable/icon-design.html
|
||||
|
||||
The last uncommented palette wins.
|
||||
*/
|
||||
|
||||
/* lighter */
|
||||
/*
|
||||
.draw-on-your-screen {
|
||||
-drawing-color1: rgb(153, 193, 241);
|
||||
-drawing-color2: rgb(143, 240, 164);
|
||||
-drawing-color3: rgb(249, 240, 107);
|
||||
-drawing-color4: rgb(255, 190, 111);
|
||||
-drawing-color5: rgb(246, 97, 81);
|
||||
-drawing-color6: rgb(220, 138, 221);
|
||||
-drawing-color7: rgb(205, 171, 143);
|
||||
-drawing-color8: rgb(255, 255, 255);
|
||||
-drawing-color9: rgb(119, 118, 123);
|
||||
}
|
||||
*/
|
||||
|
||||
/* light */
|
||||
/*
|
||||
.draw-on-your-screen {
|
||||
-drawing-color1: rgb( 98, 160, 241);
|
||||
-drawing-color2: rgb( 87, 227, 137);
|
||||
-drawing-color3: rgb(248, 228, 92);
|
||||
-drawing-color4: rgb(255, 163, 72);
|
||||
-drawing-color5: rgb(237, 51, 59);
|
||||
-drawing-color6: rgb(192, 97, 203);
|
||||
-drawing-color7: rgb(181, 131, 90);
|
||||
-drawing-color8: rgb(246, 245, 244);
|
||||
-drawing-color9: rgb( 94, 92, 100);
|
||||
}
|
||||
*/
|
||||
|
||||
/* normal */
|
||||
/*
|
||||
.draw-on-your-screen {
|
||||
-drawing-color1: rgb( 53, 132, 228);
|
||||
-drawing-color2: rgb( 51, 209, 122);
|
||||
-drawing-color3: rgb(246, 211, 45);
|
||||
-drawing-color4: rgb(255, 120, 0);
|
||||
-drawing-color5: rgb(224, 27, 36);
|
||||
-drawing-color6: rgb(145, 65, 172);
|
||||
-drawing-color7: rgb(152, 106, 68);
|
||||
-drawing-color8: rgb(222, 221, 218);
|
||||
-drawing-color9: rgb( 61, 56, 70);
|
||||
}
|
||||
*/
|
||||
|
||||
/* dark */
|
||||
/*
|
||||
.draw-on-your-screen {
|
||||
-drawing-color1: rgb( 28, 113, 216);
|
||||
-drawing-color2: rgb( 46, 194, 126);
|
||||
-drawing-color3: rgb(245, 194, 17);
|
||||
-drawing-color4: rgb(230, 97, 0);
|
||||
-drawing-color5: rgb(192, 28, 40);
|
||||
-drawing-color6: rgb(129, 61, 156);
|
||||
-drawing-color7: rgb(134, 94, 60);
|
||||
-drawing-color8: rgb(192, 191, 188);
|
||||
-drawing-color9: rgb( 36, 31, 49);
|
||||
}
|
||||
*/
|
||||
|
||||
/* darker */
|
||||
/*
|
||||
.draw-on-your-screen {
|
||||
-drawing-color1: rgb( 26, 095, 180);
|
||||
-drawing-color2: rgb( 38, 162, 105);
|
||||
-drawing-color3: rgb(229, 165, 10);
|
||||
-drawing-color4: rgb(198, 70, 0);
|
||||
-drawing-color5: rgb(165, 29, 45);
|
||||
-drawing-color6: rgb( 97, 53, 131);
|
||||
-drawing-color7: rgb( 99, 69, 44);
|
||||
-drawing-color8: rgb(154, 153, 150);
|
||||
-drawing-color9: rgb( 0, 0, 0);
|
||||
}
|
||||
*/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" version="1.1">
|
||||
<metadata>
|
||||
https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/blob/master/Papirus/symbolic/actions/tool-arc-symbolic.svg
|
||||
https://www.gnu.org/licenses/gpl-3.0.html
|
||||
</metadata>
|
||||
<path fill="#474747" d="M 14,1 C 13,1 13,2 13,2 13,2 12.941,4.04 12.701,4.6152 12.204,5.8094 11.214,7 8,7 4.2143,7 2.2036,8.8094 1.4512,10.615 1,11.698 1,14 1,14 1,14 1,15 2,15 3,15 3,14 3,14 3,14 2.9373,12.253 3.2988,11.385 3.7964,10.191 4.7857,9 8,9 11.786,9 13.796,7.1906 14.549,5.3848 15.045,4.1955 15,2 15,2 15,2 15,1 14,1 Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 604 B |
|
|
@ -7,7 +7,7 @@ Created by potrace 1.15, written by Peter Selinger 2001-2017
|
|||
https://svgsilh.com/image/1745699.html
|
||||
https://creativecommons.org/publicdomain/zero/1.0
|
||||
</metadata>
|
||||
<g fill="#555" stroke="none">
|
||||
<g fill="#474747">
|
||||
<path d="M63.3 115.3 c-8.1 -9.8 -16.3 -21.4 -22.0 -31.2 -7.3 -12.5 -11.6
|
||||
-23.5 -12.7 -32.2 -0.7 -5.2 -0.3 -9.1 1.3 -14.3 1.2 -4.0 3.1 -7.8 5.5 -11.5
|
||||
1.6 -2.4 3.0 -4.1 5.1 -6.2 3.1 -3.1 5.7 -5.0 9.3 -6.8 3.8 -1.9 7.5 -3.0
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 851 B |
|
|
@ -1,5 +1,5 @@
|
|||
<svg viewBox="0 0 576 576" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#555" stroke="transparent" x="228" y="228" width="120" height="120"/>
|
||||
<rect fill="#555" stroke="transparent" x="50" y="228" width="120" height="120"/>
|
||||
<rect fill="#555" stroke="transparent" x="406" y="228" width="120" height="120"/>
|
||||
<rect fill="#474747" x="228" y="228" width="120" height="120"/>
|
||||
<rect fill="#474747" x="50" y="228" width="120" height="120"/>
|
||||
<rect fill="#474747" x="406" y="228" width="120" height="120"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 267 B |
|
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" version="1.1">
|
||||
<metadata>
|
||||
https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/blob/master/Papirus/symbolic/actions/document-export-symbolic.svg
|
||||
https://www.gnu.org/licenses/gpl-3.0.html
|
||||
</metadata>
|
||||
<path fill="#474747" d="M 3,15 C 3,15 2,15 2,14 V 11 2 C 2,1 3,1 3,1 H 13 C 13,1 14,1 14,2 V 6.25 L 12,5 V 3.01 H 4 V 13 H 12 V 11 L 14,9.75 V 14 C 14,14 14,15 13,15 Z"/>
|
||||
<path fill="#474747" d="M 10,11 V 9 H 6 V 7 H 10 V 5 L 14.5,8 Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 517 B |
|
|
@ -1,3 +1,3 @@
|
|||
<svg viewBox="-50 -130 740 740" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<ellipse fill="#555" stroke="transparent" cx="320" cy="240" rx="320" ry="240"/>
|
||||
<ellipse fill="#474747" cx="320" cy="240" rx="320" ry="240"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 178 B |
|
|
@ -1,3 +1,3 @@
|
|||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<polygon fill="#555" stroke="transparent" fill-rule="evenodd" points="100,10 40,190 190,74 10,74 160,190"/>
|
||||
<polygon fill="#474747" fill-rule="evenodd" points="100,10 40,190 190,74 10,74 160,190"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 201 B |
|
|
@ -1,3 +1,3 @@
|
|||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<polygon fill="#555" stroke="transparent" fill-rule="nonzero" points="100,10 40,190 190,74 10,74 160,190"/>
|
||||
<polygon fill="#474747" fill-rule="nonzero" points="100,10 40,190 190,74 10,74 160,190"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 219 B After Width: | Height: | Size: 201 B |
|
|
@ -1,3 +1,3 @@
|
|||
<svg viewBox="0 0 576 576" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#555" stroke="transparent" x="50" y="228" width="476" height="120"/>
|
||||
<rect fill="#474747" x="50" y="228" width="476" height="120"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 153 B After Width: | Height: | Size: 135 B |
|
|
@ -1,4 +1,4 @@
|
|||
<svg viewBox="0 0 576 576" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#555" stroke="transparent" x="50" y="178" width="350" height="220"/>
|
||||
<ellipse fill="#555" stroke="transparent" cx="400" cy="288" rx="130" ry="110"/>
|
||||
<rect fill="#474747" x="50" y="178" width="350" height="220"/>
|
||||
<ellipse fill="#474747" cx="400" cy="288" rx="130" ry="110"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 235 B After Width: | Height: | Size: 199 B |
|
|
@ -1,3 +1,3 @@
|
|||
<svg viewBox="-50 -50 676 676" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#555" stroke="transparent" d="M -13 518 L288 0 L589 518 L500 576 L288 217 L76 576z"/>
|
||||
<path fill="#474747" d="M -13 518 L288 0 L589 518 L500 576 L288 217 L76 576z"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 174 B After Width: | Height: | Size: 156 B |
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
||||
<metadata>
|
||||
Created by potrace 1.15, written by Peter Selinger 2001-2017
|
||||
https://svgsilh.com/image/2026954.html
|
||||
https://creativecommons.org/publicdomain/zero/1.0/
|
||||
</metadata>
|
||||
<g transform="translate(0,128) scale(1,-1)" fill="#474747">
|
||||
<path d="M61.4 111.9 c-11.9 -0.8 -22.5 -5.4 -30.9 -13.6 -7.6 -7.5 -12.5
|
||||
-17 -14 -27.4 -0.4 -2.6 -0.5 -3.8 -0.5 -7 0 -4.4 0.4 -7.6 1.4 -11.7 0.9
|
||||
-3.5 1.8 -6 3.5 -9.5 1.9 -3.8 3.6 -6.5 6.4 -9.8 1.2 -1.4 4.6 -4.8 6.2
|
||||
-6.1 7.1 -5.8 15.4 -9.4 24.4 -10.6 3.1 -0.4 6.9 -0.5 8 -0.2 1.8 0.5 3.3 1.4
|
||||
4.3 2.8 1.3 1.6 1.9 3.5 1.8 5.6 -0.1 2 -0.7 3.3 -2.2 5.1 -0.7 0.8 -1.4 2.2
|
||||
-1.6 3.2 -0.3 1 -0.3 2.9 0 3.8 0.6 2.4 2.3 4.4 4.5 5.4 1.5 0.7 1.4 0.7 8.9 0.8
|
||||
3.7 0 6.9 0.1 7 0.1 0.1 0 0.7 0.1 1.3 0.2 5.4 0.9 10.5 3.5 14.5 7.5 3.2 3.3 5.4
|
||||
6.9 6.7 11.3 1.1 3.6 1.3 8.1 0.7 12.7 -1.9 13.4 -10.7 25.1 -23.8 31.8 -5.8
|
||||
3 -12 4.7 -18.7 5.4 -1.5 0.2 -6.5 0.3 -7.9 0.2z m-8.4 -10.9 c2.7 -0.9 4.7
|
||||
-3 5.5 -5.7 0.3 -1.1 0.3 -2.9 0 -4 -0.7 -2.8 -3 -5 -5.8 -5.8 -1.1 -0.3
|
||||
-2.8 -0.3 -3.9 0 -2.8 0.7 -4.8 2.7 -5.7 5.5 -0.3 0.9 -0.4 2.7 -0.2 3.7 0.5 3.1 3
|
||||
5.7 6.1 6.4 0.9 0.2 3.1 0.1 4 -0.2z m26 0.2 c3.2 -0.7 5.7 -3.3 6.2 -6.4 0.2
|
||||
-1 0.1 -2.8 -0.2 -3.7 -0.9 -2.8 -2.9 -4.7 -5.7 -5.5 -1.1 -0.3 -2.8 -0.3 -3.9 0
|
||||
-2.8 0.7 -5 2.3 -5.8 5.8 -0.3 1.1 -0.3 2.9 0 4 0.8 3 3.2 5.3 6.2 5.9 0.7
|
||||
0.1 2.4 0.1 3.2 0z m-43.1 -21.3 c1.8 -0.3 3.1 -1 4.5 -2.3 1.3 -1.3 2 -2.7
|
||||
2.3 -4.6 0.3 -2.5 -0.6 -5 -2.5 -6.7 -1 -0.9 -2 -1.5 -3.4 -1.9 -1 -0.3
|
||||
-3 -0.3 -4 0 -3.6 1 -6 4.1 -6 7.8 0 1.7 0.4 3.1 1.3 4.4 1.8 2.6 4.9
|
||||
4 7.9 3.5z m59.6 -0.2 c3.5 -1 5.8 -4.1 5.8 -7.7 0 -1.7 -0.4 -3 -1.3 -4.3
|
||||
-2.5 -3.9 -7.7 -4.8 -11.5 -2.1 -1.2 0.9 -2.4 2.5 -2.9 4 -0.9 2.7 -0.2 5.8
|
||||
1.8 7.9 1.2 1.3 2.7 2.1 4.4 2.5 1 0.2 2.7 0.1 3.6 -0.2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -1,6 +1,6 @@
|
|||
<svg viewBox="0 0 576 576" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#555" stroke="none" x="99.06" y="116.46" width="426.79" height="88.9" transform="translate(-1.13,-26.30) translate(297.56,177.87) rotate(-17.00) translate(-297.56,-177.87) translate(-14.89,16.96)"/>
|
||||
<rect fill="#555" stroke="none" x="382.92" y="219.17" width="137.39" height="55" transform="translate(-7.43,-27.23) translate(449.62,218.30) rotate(-16.48) translate(-449.62,-218.30) translate(-2,-19.20)"/>
|
||||
<rect fill="#555" stroke="none" x="99.06" y="116.46" width="426.79" height="88.9" transform="translate(0, 284.75) rotate(180) scale(1, -1) rotate(-180) translate(0, -284.75) translate(-1.13,-26.30) translate(297.56,177.87) rotate(-17.00) translate(-297.56,-177.87) translate(-14.89,16.96)"/>
|
||||
<rect fill="#555" stroke="none" x="382.92" y="219.17" width="137.39" height="55" transform="translate(0, 284.02) rotate(180) scale(1, -1) rotate(-180) translate(0, -284.02) translate(-7.43,-27.23) translate(449.62,218.30) rotate(-16.48) translate(-449.62,-218.30) translate(-2,-19.20)"/>
|
||||
<rect fill="#474747" x="99.06" y="116.46" width="426.79" height="88.9" transform="translate(296.43,151.57) rotate(-17.00) translate(-312.45,-160.91)"/>
|
||||
<rect fill="#474747" x="382.92" y="219.17" width="137.39" height="55" transform="translate(442.19,191.07) rotate(-16.48) translate(-451.62,-237.50)"/>
|
||||
<rect fill="#474747" x="99.06" y="116.46" width="426.79" height="88.9" transform="translate(0, 284.75) rotate(180) scale(1, -1) rotate(-180) translate(296.43,-133.18) rotate(-17.00) translate(-312.45,-160.91)"/>
|
||||
<rect fill="#474747" x="382.92" y="219.17" width="137.39" height="55" transform="translate(0, 284.02) rotate(180) scale(1, -1) rotate(-180) translate(442.19,-92.95) rotate(-16.48) translate(-451.62,-237.50)"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 803 B |
|
|
@ -1,3 +1,3 @@
|
|||
<svg viewBox="-50 -130 740 740" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<path stroke="transparent" fill="#555" d="m4,239.99861l0,0c0,-130.70635 141.47749,-236.66527 315.99786,-236.66527l0,0c83.80939,0 164.18481,24.93385 223.44586,69.31971c59.2608,44.38261 92.55627,104.57763 92.55627,167.34556l0,0c0,130.70897 -141.47833,236.66808 -316.00214,236.66808l0,0c-174.52037,0 -315.99786,-105.95911 -315.99786,-236.66808zm158.00044,0l0,0c0,65.35454 70.73923,118.33434 157.99742,118.33434c87.26294,0 158.00085,-52.9798 158.00085,-118.33434c0,-65.35178 -70.73792,-118.33152 -158.00085,-118.33152l0,0c-87.25819,0 -157.99742,52.97974 -157.99742,118.33152z"/>
|
||||
<path fill="#474747" d="m4,239.99861l0,0c0,-130.70635 141.47749,-236.66527 315.99786,-236.66527l0,0c83.80939,0 164.18481,24.93385 223.44586,69.31971c59.2608,44.38261 92.55627,104.57763 92.55627,167.34556l0,0c0,130.70897 -141.47833,236.66808 -316.00214,236.66808l0,0c-174.52037,0 -315.99786,-105.95911 -315.99786,-236.66808zm158.00044,0l0,0c0,65.35454 70.73923,118.33434 157.99742,118.33434c87.26294,0 158.00085,-52.9798 158.00085,-118.33434c0,-65.35178 -70.73792,-118.33152 -158.00085,-118.33152l0,0c-87.25819,0 -157.99742,52.97974 -157.99742,118.33152z"/>
|
||||
</svg>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 691 B After Width: | Height: | Size: 673 B |
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" version="1.1">
|
||||
<metadata>
|
||||
https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/blob/master/Papirus/symbolic/actions/tool-circle-move-symbolic.svg
|
||||
https://www.gnu.org/licenses/gpl-3.0.html
|
||||
</metadata>
|
||||
<path fill="#474747" d="M 8,1 A 7,7 0 0 0 1,8 7,7 0 0 0 8,15 7,7 0 0 0 15,8 7,7 0 0 0 8,1 Z M 8,3 A 5,5 0 0 1 13,8 5,5 0 0 1 8,13 5,5 0 0 1 3,8 5,5 0 0 1 8,3 Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 442 B |
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" version="1.1">
|
||||
<metadata>
|
||||
https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/blob/master/Papirus/symbolic/actions/tool-line-symbolic.svg
|
||||
https://www.gnu.org/licenses/gpl-3.0.html
|
||||
</metadata>
|
||||
<path fill="#474747" d="M 12.778,13.778 C 12.778,13.778 13.278,14.278 13.778,13.778 14.278,13.278 13.778,12.778 13.778,12.778 L 3.2237,2.2237 C 3.2237,2.2237 2.7237,1.7237 2.2237,2.2237 1.7237,2.7237 2.2237,3.2237 2.2237,3.2237 Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 505 B |
|
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" version="1.1">
|
||||
<metadata>
|
||||
https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/blob/master/Papirus/symbolic/actions/view-mirror-symbolic.svg
|
||||
https://www.gnu.org/licenses/gpl-3.0.html
|
||||
</metadata>
|
||||
<path fill="#474747" d="M 1.8613,0 C 1.0809,0 0,0.69182 0,1.5762 V 14.387 C 0,15.271 1.0809,16 1.8613,16 L 5.752,13.053 C 6.3603,12.597 7,12.324 7,11.439 V 4.5254 C 7,3.6409 6.188,3.303 5.752,2.9121 Z M 2,1.3535 5.8516,4.2598 C 5.8516,4.2598 6,4.328 6,4.5254 V 11.439 C 6,11.699 5.8984,11.699 5.8984,11.699 L 2,14.641 Z"/>
|
||||
<path fill="#474747" opacity="0.3" d="M 13.139,0 C 13.919,0 15,0.69182 15,1.5762 V 14.387 C 15,15.271 13.919,16 13.139,16 L 9.248,13.053 C 8.6397,12.597 8,12.324 8,11.439 V 4.5254 C 8,3.6409 8.812,3.303 9.248,2.9121 Z M 13,1.3535 9.1484,4.2598 C 9.1484,4.2598 9,4.328 9,4.5254 V 11.439 C 9,11.699 9.1016,11.699 9.1016,11.699 L 13,14.641 Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 940 B |
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
|
||||
<metadata>
|
||||
Combination of https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/blob/master/Papirus/symbolic/actions/object-move-symbolic.svg
|
||||
https://www.gnu.org/licenses/gpl-3.0.html
|
||||
and https://gitlab.gnome.org/World/design/icon-library/-/blob/master/data/resources/icon-dev-kit/rotate-symbolic.svg
|
||||
https://www.gnu.org/licenses/gpl-3.0.html
|
||||
optimized with SVGO
|
||||
</metadata>
|
||||
<path d="M8 15.5L5 12h2v-2h2v2h2zM.5 8L4 5v2h2v2H4v2z" fill="#474747"/>
|
||||
<a transform="rotate(-4.342 5.603 3.916)">
|
||||
<path fill="#474747" d="M8.672.954l-3.654 3.2c.634.717 1.341 1.557 1.92 2.193l1.28 1.46.166-2.484.022-.02c.769-.196 1.74.097 2.457.915 1.102 1.259.67 2.758-.634 3.9l.57.674c.346.407 1.058.4 1.396-.012 1.65-1.444 1.556-4.028.037-5.763-.986-1.124-2.398-1.707-3.725-1.579z"/>
|
||||
</a>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 903 B |
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 920.729 920.729">
|
||||
<metadata>
|
||||
Combination of https://www.svgrepo.com/svg/150374/pencil
|
||||
https://creativecommons.org/publicdomain/zero/1.0/deed.en
|
||||
and https://www.svgrepo.com/svg/29291/pencil
|
||||
https://creativecommons.org/publicdomain/zero/1.0/deed.en
|
||||
</metadata>
|
||||
<g fill="#474747">
|
||||
<path d="m 823.25922,841.48926 c -1.399,0 -2.699,0.1 -4.1,0.3 l -245.7,31.8 c -12.6,1.601 -21.399,-12.2 -14.6,-23 3.7,-5.7 7.3,-11.5 10.899,-17.3 9.5,-15.4 -3.199,-35.9 -20.8,-35.9 -1.2,0 -2.399,0.101 -3.7,0.301 l -322,48.399 -38.5,5.8 -104.999997,15.801 c -11.9,1.8 -19.9,11.899 -18.9,24 0.4,5.399 2.6,10.5 6.1,14.3 4.1,4.5 9.8,7 16,7 1.2,0 2.4,-0.101 3.7,-0.3 l 383.799997,-57.7 c 13.101,-2 22.101,12.8 14.4,23.6 -2.5,3.4 -5,6.9 -7.5,10.3 -6.9,9.4 -5.3,25.101 2.7,33.2 4.1,4.2 9.3,6.4 14.8,6.4 0.6,0 1.3,0 1.899,-0.101 h 0.101 0.1 l 331.3,-42.199 h 0.101 0.1 c 5.7,-0.9 10.7,-4.2 14,-9.301 3.601,-5.6 4.9,-12.5 3.3,-18.699 -2.6,-10.101 -11.4,-16.701 -22.5,-16.701 z"/>
|
||||
<g transform="translate(58.371053,4.3729293)">
|
||||
<path d="m 515.582,157.916 -439.199,439.2 4.6,10.1 27.1,58.8 41.6,22 c 9.8,5.2 17.7,13.101 22.9,22.9 l 22,41.6 58.8,27.101 10.101,4.6 439.2,-439.2 z"/>
|
||||
<path d="m 853.282,159.216 -151.8,-151.8 c -4.9,-4.9 -11.3,-7.3 -17.7,-7.3 -6.4,0 -12.8,2.4 -17.7,7.3 l -129.3,129.3 187.2,187.2 129.3,-129.3 c 9.8,-9.8 9.8,-25.6 0,-35.4 z"/>
|
||||
<path d="m 46.083,650.016 -4.3,16.9 -41,162.5 c -4.1,16.2 8.5,31.1 24.1,31.1 2,0 4.1,-0.3 6.2,-0.8 l 162.5,-41 16.9,-4.3 16.9,-4.3 13.3,-3.4 -30.9,-14.2 -29.5,-13.5 c -5,-2.3 -9.1,-6.199 -11.7,-11 l -18.6,-35.1 -4.3,-8 c -2.3,-4.4 -6,-8.1 -10.4,-10.4 l -8,-4.3 -35.1,-18.6 c -4.9,-2.601 -8.7,-6.7 -11,-11.7 l -13.5,-29.5 -14.2,-30.9 -3.4,13.301 z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" version="1.1">
|
||||
<metadata>
|
||||
https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/blob/master/Papirus/symbolic/actions/tool-polygon-symbolic.svg
|
||||
https://www.gnu.org/licenses/gpl-3.0.html
|
||||
</metadata>
|
||||
<path fill="#474747" d="M 12.98,2.5 1.9551,3.502 1.5,4 V 14 L 2.3203,14.385 8.0684,9.5938 13.777,12.447 14.496,11.945 13.496,2.9453 Z M 12.557,3.543 13.402,11.143 8.2227,8.5527 7.6797,8.6152 2.5,12.932 V 4.457 Z"/>
|
||||
<path fill="#474747" d="M 10,9 A 2,2 0 0 1 8,11 2,2 0 0 1 6,9 2,2 0 0 1 8,7 2,2 0 0 1 10,9 Z M 4,4 A 2,2 0 0 1 2,6 2,2 0 0 1 0,4 2,2 0 0 1 2,2 2,2 0 0 1 4,4 Z M 4,14 A 2,2 0 0 1 2,16 2,2 0 0 1 0,14 2,2 0 0 1 2,12 2,2 0 0 1 4,14 Z M 16,12 A 2,2 0 0 1 14,14 2,2 0 0 1 12,12 2,2 0 0 1 14,10 2,2 0 0 1 16,12 Z M 15,3 A 2,2 0 0 1 13,5 2,2 0 0 1 11,3 2,2 0 0 1 13,1 2,2 0 0 1 15,3 Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 871 B |
|
|
@ -0,0 +1,9 @@
|
|||
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#474747" x="98" y="204" width="226" height="40" transform="translate(116,253) rotate(-66) translate(-211,-226)"/>
|
||||
<rect fill="#474747" x="145" y="339" width="270" height="40" transform="translate(236,262) rotate(58) translate(-280,-362)"/>
|
||||
<rect fill="#474747" x="141" y="429" width="211" height="40" transform="translate(374,300) rotate(-47) translate(-246,-451)"/>
|
||||
<circle fill="#474747" cx="166" cy="172" r="55"/>
|
||||
<circle fill="#474747" cx="166" cy="172" r="55" transform="translate(143,190)"/>
|
||||
<circle fill="#474747" cx="166" cy="172" r="55" transform="translate(-87,168)"/>
|
||||
<circle fill="#474747" cx="166" cy="172" r="55" transform="translate(273,56)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 755 B |
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" version="1.1">
|
||||
<metadata>
|
||||
https://github.com/PapirusDevelopmentTeam/papirus-icon-theme/blob/master/Papirus/symbolic/actions/tool-rectangle-symbolic.svg
|
||||
https://www.gnu.org/licenses/gpl-3.0.html
|
||||
</metadata>
|
||||
<path fill="#474747" d="M 1,1 V 15 H 15 V 1 Z M 3,3 H 13 V 13 H 3 Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 348 B |
|
|
@ -0,0 +1,15 @@
|
|||
<svg width="16" height="16">
|
||||
<metadata>
|
||||
https://gitlab.gnome.org/GNOME/adwaita-icon-theme/-/blob/master/Adwaita/scalable/actions/view-fullscreen-symbolic.svg
|
||||
https://creativecommons.org/licenses/by-sa/3.0/
|
||||
</metadata>
|
||||
<g fill="#474747">
|
||||
<path d="M1.984 8.986A1 1 0 0 0 1 10v4a1 1 0 0 0 1 1h4a1 1 0 1 0 0-2H3v-3a1 1 0 0 0-1.016-1.014z" />
|
||||
<path d="M6.48 8.49a1 1 0 0 0-.687.303l-4.5 4.5a1 1 0 1 0 1.414 1.414l4.5-4.5A1 1 0 0 0 6.48 8.49z" />
|
||||
<path d="M1 14h1v1H1z" />
|
||||
<path d="M10 1a1 1 0 1 0 0 2h3v3a1 1 0 1 0 2 0V2a1 1 0 0 0-1-1z" />
|
||||
<path d="M14 1h1v1h-1z" />
|
||||
<path d="M13.984.99a1 1 0 0 0-.69.301l-4.5 4.469a1 1 0 1 0 1.411 1.418l4.5-4.469a1 1 0 0 0-.72-1.719z" />
|
||||
<path d="M1 9h1v1H1zM6 14h1v1H6zM14 6h1v1h-1zM9 1h1v1H9z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 773 B |
210
elements.js
|
|
@ -1,4 +1,5 @@
|
|||
/* jslint esversion: 6 */
|
||||
/* exported Shapes, Transformations, getAllFontFamilies, DrawingElement */
|
||||
|
||||
/*
|
||||
* Copyright 2019 Abakkk
|
||||
|
|
@ -26,28 +27,27 @@ const Lang = imports.lang;
|
|||
const Pango = imports.gi.Pango;
|
||||
const PangoCairo = imports.gi.PangoCairo;
|
||||
|
||||
const reverseEnumeration = function(obj) {
|
||||
let reversed = {};
|
||||
Object.keys(obj).forEach(key => {
|
||||
reversed[obj[key]] = key.slice(0,1) + key.slice(1).toLowerCase().replace('_', '-');
|
||||
});
|
||||
return reversed;
|
||||
var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6, IMAGE: 7 };
|
||||
var Transformations = { TRANSLATION: 0, ROTATION: 1, SCALE_PRESERVE: 2, STRETCH: 3, REFLECTION: 4, INVERSION: 5 };
|
||||
|
||||
var getAllFontFamilies = function() {
|
||||
return PangoCairo.font_map_get_default().list_families().map(fontFamily => fontFamily.get_name()).sort((a,b) => a.localeCompare(b));
|
||||
};
|
||||
|
||||
var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6, IMAGE: 7 };
|
||||
var ShapeNames = { 0: "Free drawing", 1: "Line", 2: "Ellipse", 3: "Rectangle", 4: "Text", 5: "Polygon", 6: "Polyline", 7: "Image" };
|
||||
var Transformations = { TRANSLATION: 0, ROTATION: 1, SCALE_PRESERVE: 2, STRETCH: 3, REFLECTION: 4, INVERSION: 5 };
|
||||
var LineCapNames = Object.assign(reverseEnumeration(Cairo.LineCap), { 2: 'Square' });
|
||||
var LineJoinNames = reverseEnumeration(Cairo.LineJoin);
|
||||
var FillRuleNames = { 0: 'Nonzero', 1: 'Evenodd' };
|
||||
var FontWeightNames = Object.assign(reverseEnumeration(Pango.Weight), { 200: "Ultra-light", 350: "Semi-light", 600: "Semi-bold", 800: "Ultra-bold" });
|
||||
delete FontWeightNames[Pango.Weight.ULTRAHEAVY];
|
||||
var FontStyleNames = reverseEnumeration(Pango.Style);
|
||||
var FontStretchNames = reverseEnumeration(Pango.Stretch);
|
||||
var FontVariantNames = reverseEnumeration(Pango.Variant);
|
||||
const getFillRuleSvgName = function(fillRule) {
|
||||
return fillRule == Cairo.FillRule.EVEN_ODD ? 'evenodd' : 'nonzero';
|
||||
};
|
||||
|
||||
var getPangoFontFamilies = function() {
|
||||
return PangoCairo.font_map_get_default().list_families().map(fontFamily => fontFamily.get_name()).sort((a,b) => a.localeCompare(b));
|
||||
const getLineCapSvgName = function(lineCap) {
|
||||
return lineCap == Cairo.LineCap.BUTT ? 'butt' :
|
||||
lineCap == Cairo.LineCap.SQUASH ? 'square' :
|
||||
'round';
|
||||
};
|
||||
|
||||
const getLineJoinSvgName = function(lineJoin) {
|
||||
return lineJoin == Cairo.LineJoin.MITER ? 'miter' :
|
||||
lineJoin == Cairo.LineJoin.BEVEL ? 'bevel' :
|
||||
'round';
|
||||
};
|
||||
|
||||
const SVG_DEBUG_SUPERPOSES_CAIRO = false;
|
||||
|
|
@ -80,10 +80,21 @@ const _DrawingElement = new Lang.Class({
|
|||
|
||||
if (params.transformations === undefined)
|
||||
this.transformations = [];
|
||||
if (params.font && params.font.weight === 0)
|
||||
this.font.weight = 400;
|
||||
if (params.font && params.font.weight === 1)
|
||||
this.font.weight = 700;
|
||||
|
||||
if (params.font && !(params.font instanceof Pango.FontDescription)) {
|
||||
// compatibility with v6.2-
|
||||
if (params.font.weight === 0)
|
||||
this.font.weight = 400;
|
||||
else if (params.font.weight === 1)
|
||||
this.font.weight = 700;
|
||||
this.font = new Pango.FontDescription();
|
||||
['family', 'weight', 'style', 'stretch', 'variant'].forEach(attribute => {
|
||||
if (params.font[attribute] !== undefined)
|
||||
try {
|
||||
this.font[`set_${attribute}`](params.font[attribute]);
|
||||
} catch(e) {}
|
||||
});
|
||||
}
|
||||
|
||||
if (params.transform && params.transform.center) {
|
||||
let angle = (params.transform.angle || 0) + (params.transform.startAngle || 0);
|
||||
|
|
@ -102,7 +113,7 @@ const _DrawingElement = new Lang.Class({
|
|||
toJSON: function() {
|
||||
return {
|
||||
shape: this.shape,
|
||||
color: this.color,
|
||||
color: this.color.toString(),
|
||||
line: this.line,
|
||||
dash: this.dash,
|
||||
fill: this.fill,
|
||||
|
|
@ -114,11 +125,8 @@ const _DrawingElement = new Lang.Class({
|
|||
},
|
||||
|
||||
buildCairo: function(cr, params) {
|
||||
if (this.color) {
|
||||
let [success, color] = Clutter.Color.from_string(this.color);
|
||||
if (success)
|
||||
Clutter.cairo_set_source_color(cr, color);
|
||||
}
|
||||
if (this.color)
|
||||
Clutter.cairo_set_source_color(cr, this.color);
|
||||
|
||||
if (this.showSymmetryElement) {
|
||||
let transformation = this.lastTransformation;
|
||||
|
|
@ -244,63 +252,89 @@ const _DrawingElement = new Lang.Class({
|
|||
return inElement;
|
||||
},
|
||||
|
||||
buildSVG: function(bgColor) {
|
||||
let transAttribute = '';
|
||||
buildSVG: function(bgcolorString) {
|
||||
let transforms = [];
|
||||
this.transformations.slice(0).reverse().forEach(transformation => {
|
||||
transAttribute += transAttribute ? ' ' : ' transform="';
|
||||
let center = this._getTransformedCenter(transformation);
|
||||
|
||||
if (transformation.type == Transformations.TRANSLATION) {
|
||||
transAttribute += `translate(${transformation.slideX},${transformation.slideY})`;
|
||||
transforms.push(['translate', transformation.slideX, transformation.slideY]);
|
||||
} else if (transformation.type == Transformations.ROTATION) {
|
||||
transAttribute += `translate(${center[0]},${center[1]}) `;
|
||||
transAttribute += `rotate(${transformation.angle * RADIAN}) `;
|
||||
transAttribute += `translate(${-center[0]},${-center[1]})`;
|
||||
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) {
|
||||
transAttribute += `translate(${center[0]},${center[1]}) `;
|
||||
transAttribute += `rotate(${transformation.angle * RADIAN}) `;
|
||||
transAttribute += `scale(${transformation.scaleX},${transformation.scaleY}) `;
|
||||
transAttribute += `rotate(${-transformation.angle * RADIAN}) `;
|
||||
transAttribute += `translate(${-center[0]},${-center[1]})`;
|
||||
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) {
|
||||
transAttribute += `translate(${transformation.slideX}, ${transformation.slideY}) `;
|
||||
transAttribute += `rotate(${transformation.angle * RADIAN}) `;
|
||||
transAttribute += `scale(${transformation.scaleX}, ${transformation.scaleY}) `;
|
||||
transAttribute += `rotate(${-transformation.angle * RADIAN}) `;
|
||||
transAttribute += `translate(${-transformation.slideX}, ${-transformation.slideY})`;
|
||||
transforms.push(['translate', transformation.slideX, transformation.slideY]);
|
||||
transforms.push(['rotate', transformation.angle * RADIAN]);
|
||||
transforms.push(['scale', transformation.scaleX, transformation.scaleY]);
|
||||
transforms.push(['rotate', -transformation.angle * RADIAN]);
|
||||
transforms.push(['translate', -transformation.slideX, -transformation.slideY]);
|
||||
}
|
||||
});
|
||||
transAttribute += transAttribute ? '"' : '';
|
||||
|
||||
let grouped = [];
|
||||
transforms.forEach((transform, index) => {
|
||||
let [type, ...values] = transform;
|
||||
|
||||
if (grouped.length && grouped[grouped.length - 1][0] == type)
|
||||
values.forEach((value, valueIndex) => grouped[grouped.length - 1][valueIndex + 1] += value);
|
||||
else
|
||||
grouped.push(transform);
|
||||
});
|
||||
|
||||
return this._drawSvg(transAttribute);
|
||||
let filtered = grouped.filter(transform => {
|
||||
let [type, ...values] = transform;
|
||||
|
||||
if (type == 'scale')
|
||||
return values.some(value => value != 1);
|
||||
else
|
||||
return values.some(value => value != 0);
|
||||
});
|
||||
|
||||
let transAttribute = '';
|
||||
if (filtered.length) {
|
||||
transAttribute = ' transform="';
|
||||
filtered.forEach((transform, index) => {
|
||||
let [type, ...values] = transform;
|
||||
transAttribute += `${index == 0 ? '' : ' '}${type}(${values.map(value => Number(value).toFixed(2))})`;
|
||||
});
|
||||
|
||||
transAttribute += '"';
|
||||
}
|
||||
|
||||
return this._drawSvg(transAttribute, bgcolorString);
|
||||
},
|
||||
|
||||
_drawSvg: function(transAttribute) {
|
||||
_drawSvg: function(transAttribute, bgcolorString) {
|
||||
let row = "\n ";
|
||||
let points = this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100]);
|
||||
let color = this.eraser ? bgColor : this.color;
|
||||
let color = this.eraser ? bgcolorString : this.color.toString();
|
||||
let fill = this.fill && !this.isStraightLine;
|
||||
let attributes = '';
|
||||
let attributes = this.eraser ? `class="eraser" ` : '';
|
||||
|
||||
if (fill) {
|
||||
attributes = `fill="${color}"`;
|
||||
attributes += `fill="${color}"`;
|
||||
if (this.fillRule)
|
||||
attributes += ` fill-rule="${FillRuleNames[this.fillRule].toLowerCase()}"`;
|
||||
attributes += ` fill-rule="${getFillRuleSvgName(this.fillRule)}"`;
|
||||
} else {
|
||||
attributes = `fill="none"`;
|
||||
attributes += `fill="none"`;
|
||||
}
|
||||
|
||||
if (this.line && this.line.lineWidth) {
|
||||
attributes += ` stroke="${color}"` +
|
||||
` stroke-width="${this.line.lineWidth}"`;
|
||||
if (this.line.lineCap)
|
||||
attributes += ` stroke-linecap="${LineCapNames[this.line.lineCap].toLowerCase()}"`;
|
||||
attributes += ` stroke-linecap="${getLineCapSvgName(this.line.lineCap)}"`;
|
||||
if (this.line.lineJoin && !this.isStraightLine)
|
||||
attributes += ` stroke-linejoin="${LineJoinNames[this.line.lineJoin].toLowerCase()}"`;
|
||||
attributes += ` stroke-linejoin="${getLineJoinSvgName(this.line.lineJoin)}"`;
|
||||
if (this.dash && this.dash.active && this.dash.array && this.dash.array[0] && this.dash.array[1])
|
||||
attributes += ` stroke-dasharray="${this.dash.array[0]} ${this.dash.array[1]}" stroke-dashoffset="${this.dash.offset}"`;
|
||||
} else {
|
||||
attributes += ` stroke="none"`;
|
||||
}
|
||||
|
||||
if (this.shape == Shapes.LINE && points.length == 4) {
|
||||
|
|
@ -594,15 +628,18 @@ const TextElement = new Lang.Class({
|
|||
Extends: _DrawingElement,
|
||||
|
||||
toJSON: function() {
|
||||
// The font size is useless because it is always computed from the points during cairo/svg building.
|
||||
this.font.unset_fields(Pango.FontMask.SIZE);
|
||||
|
||||
return {
|
||||
shape: this.shape,
|
||||
color: this.color,
|
||||
color: this.color.toString(),
|
||||
eraser: this.eraser,
|
||||
transformations: this.transformations,
|
||||
text: this.text,
|
||||
lineIndex: this.lineIndex !== undefined ? this.lineIndex : undefined,
|
||||
textRightAligned: this.textRightAligned,
|
||||
font: this.font,
|
||||
font: this.font.to_string(),
|
||||
points: this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100])
|
||||
};
|
||||
},
|
||||
|
|
@ -629,15 +666,8 @@ const TextElement = new Lang.Class({
|
|||
if (this.points.length == 2) {
|
||||
let layout = PangoCairo.create_layout(cr);
|
||||
let fontSize = this.height * Pango.SCALE;
|
||||
let fontDescription = new Pango.FontDescription();
|
||||
fontDescription.set_absolute_size(fontSize);
|
||||
['family', 'weight', 'style', 'stretch', 'variant'].forEach(attribute => {
|
||||
if (this.font[attribute] !== undefined)
|
||||
try {
|
||||
fontDescription[`set_${attribute}`](this.font[attribute]);
|
||||
} catch(e) {}
|
||||
});
|
||||
layout.set_font_description(fontDescription);
|
||||
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.get_baseline() / Pango.SCALE);
|
||||
|
|
@ -670,29 +700,29 @@ const TextElement = new Lang.Class({
|
|||
return cr.inFill(x, y);
|
||||
},
|
||||
|
||||
_drawSvg: function(transAttribute) {
|
||||
_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];
|
||||
let color = this.eraser ? bgColor : this.color;
|
||||
let attributes = '';
|
||||
let color = this.eraser ? bgcolorString : this.color.toString();
|
||||
let attributes = this.eraser ? `class="eraser" ` : '';
|
||||
|
||||
if (this.points.length == 2) {
|
||||
attributes = `fill="${color}" ` +
|
||||
`stroke="transparent" ` +
|
||||
`stroke-opacity="0" ` +
|
||||
`font-size="${height}"`;
|
||||
|
||||
if (this.font.family)
|
||||
attributes += ` font-family="${this.font.family}"`;
|
||||
if (this.font.weight && this.font.weight != Pango.Weight.NORMAL)
|
||||
attributes += ` font-weight="${this.font.weight}"`;
|
||||
if (this.font.style && FontStyleNames[this.font.style])
|
||||
attributes += ` font-style="${FontStyleNames[this.font.style].toLowerCase()}"`;
|
||||
if (FontStretchNames[this.font.stretch] && this.font.stretch != Pango.Stretch.NORMAL)
|
||||
attributes += ` font-stretch="${FontStretchNames[this.font.stretch].toLowerCase()}"`;
|
||||
if (this.font.variant && FontVariantNames[this.font.variant])
|
||||
attributes += ` font-variant="${FontVariantNames[this.font.variant].toLowerCase()}"`;
|
||||
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 += `<text ${attributes} x="${x}" `;
|
||||
row += `y="${y}"${transAttribute}>${this.text}</text>`;
|
||||
}
|
||||
|
|
@ -745,7 +775,7 @@ const ImageElement = new Lang.Class({
|
|||
toJSON: function() {
|
||||
return {
|
||||
shape: this.shape,
|
||||
color: this.color,
|
||||
color: this.color.toString(),
|
||||
fill: this.fill,
|
||||
eraser: this.eraser,
|
||||
transformations: this.transformations,
|
||||
|
|
@ -789,10 +819,10 @@ const ImageElement = new Lang.Class({
|
|||
_drawSvg: function(transAttribute) {
|
||||
let points = this.points;
|
||||
let row = "\n ";
|
||||
let attributes = '';
|
||||
let attributes = this.eraser ? `class="eraser" ` : '';
|
||||
|
||||
if (points.length == 2) {
|
||||
attributes = `fill="none"`;
|
||||
attributes += `fill="none"`;
|
||||
row += `<image ${attributes} x="${Math.min(points[0][0], points[1][0])}" y="${Math.min(points[0][1], points[1][1])}" ` +
|
||||
`width="${Math.abs(points[1][0] - points[0][0])}" height="${Math.abs(points[1][1] - points[0][1])}"${transAttribute} ` +
|
||||
`preserveAspectRatio="${this.preserveAspectRatio ? 'xMinYMin' : 'none'}" ` +
|
||||
|
|
|
|||
232
extension.js
|
|
@ -1,4 +1,5 @@
|
|||
/* jslint esversion: 6 */
|
||||
/* exported init */
|
||||
|
||||
/*
|
||||
* Copyright 2019 Abakkk
|
||||
|
|
@ -20,22 +21,21 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Main = imports.ui.main;
|
||||
const OsdWindow = imports.ui.osdWindow;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = ExtensionUtils.getSettings && ExtensionUtils.initTranslations ? ExtensionUtils : Me.imports.convenience;
|
||||
const Area = Me.imports.area;
|
||||
const Files = Me.imports.files;
|
||||
const Helper = Me.imports.helper;
|
||||
const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext;
|
||||
|
||||
|
|
@ -46,102 +46,114 @@ const HIDE_TIMEOUT_LONG = 2500; // ms, default is 1500 ms
|
|||
const DRAWING_ACTION_MODE = Math.pow(2,14);
|
||||
const WRITING_ACTION_MODE = Math.pow(2,15);
|
||||
// use 'login-dialog-message-warning' class in order to get GS theme warning color (default: #f57900)
|
||||
var WARNING_COLOR_STYLE_CLASS_NAME = 'login-dialog-message-warning';
|
||||
|
||||
var manager;
|
||||
const WARNING_COLOR_STYLE_CLASS_NAME = 'login-dialog-message-warning';
|
||||
|
||||
function init() {
|
||||
Convenience.initTranslations();
|
||||
return new Extension();
|
||||
}
|
||||
|
||||
function enable() {
|
||||
manager = new AreaManager();
|
||||
}
|
||||
const Extension = new Lang.Class({
|
||||
Name: 'DrawOnYourScreenExtension',
|
||||
|
||||
_init: function() {
|
||||
Convenience.initTranslations();
|
||||
},
|
||||
|
||||
function disable() {
|
||||
manager.disable();
|
||||
manager = null;
|
||||
}
|
||||
enable() {
|
||||
if (ExtensionUtils.isOutOfDate(Me))
|
||||
log(`${Me.metadata.uuid}: GNOME Shell ${Number.parseFloat(GS_VERSION)} is not supported.`);
|
||||
|
||||
Me.settings = Convenience.getSettings();
|
||||
Me.internalShortcutSettings = Convenience.getSettings(Me.metadata['settings-schema'] + '.internal-shortcuts');
|
||||
Me.drawingSettings = Convenience.getSettings(Me.metadata['settings-schema'] + '.drawing');
|
||||
this.areaManager = new AreaManager();
|
||||
},
|
||||
|
||||
disable() {
|
||||
this.areaManager.disable();
|
||||
delete this.areaManager;
|
||||
delete Me.settings;
|
||||
delete Me.internalShortcutSettings;
|
||||
}
|
||||
});
|
||||
|
||||
// AreaManager assigns one DrawingArea per monitor (updateAreas()),
|
||||
// distributes keybinding callbacks to the active area
|
||||
// and handles stylesheet and monitor changes.
|
||||
var AreaManager = new Lang.Class({
|
||||
const AreaManager = new Lang.Class({
|
||||
Name: 'DrawOnYourScreenAreaManager',
|
||||
|
||||
_init: function() {
|
||||
this.settings = Convenience.getSettings();
|
||||
this.areas = [];
|
||||
this.activeArea = null;
|
||||
this.enterGicon = new Gio.ThemedIcon({ name: 'applications-graphics-symbolic' });
|
||||
this.leaveGicon = new Gio.ThemedIcon({ name: 'application-exit-symbolic' });
|
||||
|
||||
Main.wm.addKeybinding('toggle-drawing',
|
||||
this.settings,
|
||||
Me.settings,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.ActionMode.ALL,
|
||||
this.toggleDrawing.bind(this));
|
||||
|
||||
Main.wm.addKeybinding('toggle-modal',
|
||||
this.settings,
|
||||
Me.settings,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.ActionMode.ALL,
|
||||
this.toggleModal.bind(this));
|
||||
|
||||
Main.wm.addKeybinding('erase-drawing',
|
||||
this.settings,
|
||||
Main.wm.addKeybinding('erase-drawings',
|
||||
Me.settings,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.ActionMode.ALL,
|
||||
this.eraseDrawing.bind(this));
|
||||
this.eraseDrawings.bind(this));
|
||||
|
||||
this.updateAreas();
|
||||
this.monitorChangedHandler = Main.layoutManager.connect('monitors-changed', this.updateAreas.bind(this));
|
||||
|
||||
this.updateIndicator();
|
||||
this.indicatorSettingHandler = this.settings.connect('changed::indicator-disabled', this.updateIndicator.bind(this));
|
||||
this.indicatorSettingHandler = Me.settings.connect('changed::indicator-disabled', this.updateIndicator.bind(this));
|
||||
|
||||
this.desktopSettingHandler = this.settings.connect('changed::drawing-on-desktop', this.onDesktopSettingChanged.bind(this));
|
||||
this.persistentSettingHandler = this.settings.connect('changed::persistent-drawing', this.onPersistentSettingChanged.bind(this));
|
||||
|
||||
this.userStyleFile = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir'], 'user.css']));
|
||||
|
||||
if (this.userStyleFile.query_exists(null)) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
theme.load_stylesheet(this.userStyleFile);
|
||||
}
|
||||
|
||||
this.userStyleMonitor = this.userStyleFile.monitor_file(Gio.FileMonitorFlags.WATCH_MOVES, null);
|
||||
this.userStyleHandler = this.userStyleMonitor.connect('changed', (monitor, file, otherFile, eventType) => {
|
||||
// 'CHANGED' events are followed by a 'CHANGES_DONE_HINT' event
|
||||
if (eventType == Gio.FileMonitorEvent.CHANGED || eventType == Gio.FileMonitorEvent.ATTRIBUTE_CHANGED)
|
||||
return;
|
||||
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
if (theme.get_custom_stylesheets().indexOf(this.userStyleFile) != -1)
|
||||
theme.unload_stylesheet(this.userStyleFile);
|
||||
if (this.userStyleFile.query_exists(null))
|
||||
theme.load_stylesheet(this.userStyleFile);
|
||||
});
|
||||
this.desktopSettingHandler = Me.settings.connect('changed::drawing-on-desktop', this.onDesktopSettingChanged.bind(this));
|
||||
this.persistentOverRestartsSettingHandler = Me.settings.connect('changed::persistent-over-restarts', this.onPersistentOverRestartsSettingChanged.bind(this));
|
||||
this.persistentOverTogglesSettingHandler = Me.settings.connect('changed::persistent-over-toggles', this.onPersistentOverTogglesSettingChanged.bind(this));
|
||||
},
|
||||
|
||||
get persistentOverToggles() {
|
||||
return Me.settings.get_boolean('persistent-over-toggles');
|
||||
},
|
||||
|
||||
get persistentOverRestarts() {
|
||||
return Me.settings.get_boolean('persistent-over-toggles') && Me.settings.get_boolean('persistent-over-restarts');
|
||||
},
|
||||
|
||||
get onDesktop() {
|
||||
return Me.settings.get_boolean('persistent-over-toggles') && Me.settings.get_boolean('drawing-on-desktop');
|
||||
},
|
||||
|
||||
onDesktopSettingChanged: function() {
|
||||
if (this.settings.get_boolean("drawing-on-desktop"))
|
||||
if (this.onDesktop)
|
||||
this.areas.forEach(area => area.get_parent().show());
|
||||
else
|
||||
this.areas.forEach(area => area.get_parent().hide());
|
||||
},
|
||||
|
||||
onPersistentSettingChanged: function() {
|
||||
if (this.settings.get_boolean('persistent-drawing'))
|
||||
onPersistentOverRestartsSettingChanged: function() {
|
||||
if (this.persistentOverRestarts)
|
||||
this.areas[Main.layoutManager.primaryIndex].syncPersistent();
|
||||
},
|
||||
|
||||
onPersistentOverTogglesSettingChanged: function() {
|
||||
if (!this.persistentOverToggles && !this.activeArea)
|
||||
this.eraseDrawings();
|
||||
|
||||
this.onPersistentOverRestartsSettingChanged();
|
||||
this.onDesktopSettingChanged();
|
||||
},
|
||||
|
||||
updateIndicator: function() {
|
||||
if (this.indicator) {
|
||||
this.indicator.disable();
|
||||
this.indicator = null;
|
||||
}
|
||||
if (!this.settings.get_boolean('indicator-disabled'))
|
||||
if (!Me.settings.get_boolean('indicator-disabled'))
|
||||
this.indicator = new DrawingIndicator();
|
||||
},
|
||||
|
||||
|
|
@ -156,13 +168,13 @@ var AreaManager = new Lang.Class({
|
|||
let monitor = this.monitors[i];
|
||||
let container = new St.Widget({ name: 'drawOnYourSreenContainer' + i });
|
||||
let helper = new Helper.DrawingHelper({ name: 'drawOnYourSreenHelper' + i }, monitor);
|
||||
let loadPersistent = i == Main.layoutManager.primaryIndex && this.settings.get_boolean('persistent-drawing');
|
||||
let loadPersistent = i == Main.layoutManager.primaryIndex && this.persistentOverRestarts;
|
||||
let area = new Area.DrawingArea({ name: 'drawOnYourSreenArea' + i }, monitor, helper, loadPersistent);
|
||||
container.add_child(area);
|
||||
container.add_child(helper);
|
||||
|
||||
Main.layoutManager._backgroundGroup.insert_child_above(container, Main.layoutManager._bgManagers[i].backgroundActor);
|
||||
if (!this.settings.get_boolean("drawing-on-desktop"))
|
||||
if (!this.onDesktop)
|
||||
container.hide();
|
||||
|
||||
container.set_position(monitor.x, monitor.y);
|
||||
|
|
@ -171,7 +183,6 @@ var AreaManager = new Lang.Class({
|
|||
area.leaveDrawingHandler = area.connect('leave-drawing-mode', this.toggleDrawing.bind(this));
|
||||
area.updateActionModeHandler = area.connect('update-action-mode', this.updateActionMode.bind(this));
|
||||
area.showOsdHandler = area.connect('show-osd', this.showOsd.bind(this));
|
||||
area.showOsdGiconHandler = area.connect('show-osd-gicon', this.showOsd.bind(this));
|
||||
this.areas.push(area);
|
||||
}
|
||||
},
|
||||
|
|
@ -187,12 +198,14 @@ var AreaManager = new Lang.Class({
|
|||
'decrement-line-width': () => this.activeArea.incrementLineWidth(-1),
|
||||
'increment-line-width-more': () => this.activeArea.incrementLineWidth(5),
|
||||
'decrement-line-width-more': () => this.activeArea.incrementLineWidth(-5),
|
||||
'paste-image-files': this.activeArea.pasteImageFiles.bind(this.activeArea),
|
||||
'switch-linejoin': this.activeArea.switchLineJoin.bind(this.activeArea),
|
||||
'switch-linecap': this.activeArea.switchLineCap.bind(this.activeArea),
|
||||
'switch-fill-rule': this.activeArea.switchFillRule.bind(this.activeArea),
|
||||
'switch-dash' : this.activeArea.switchDash.bind(this.activeArea),
|
||||
'switch-fill' : this.activeArea.switchFill.bind(this.activeArea),
|
||||
'switch-image-file' : this.activeArea.switchImageFile.bind(this.activeArea),
|
||||
'switch-image-file' : this.activeArea.switchImageFile.bind(this.activeArea, false),
|
||||
'switch-image-file-reverse' : this.activeArea.switchImageFile.bind(this.activeArea, true),
|
||||
'select-none-shape': () => this.activeArea.selectTool(Area.Tools.NONE),
|
||||
'select-line-shape': () => this.activeArea.selectTool(Area.Tools.LINE),
|
||||
'select-ellipse-shape': () => this.activeArea.selectTool(Area.Tools.ELLIPSE),
|
||||
|
|
@ -208,27 +221,28 @@ var AreaManager = new Lang.Class({
|
|||
|
||||
// available when writing
|
||||
this.internalKeybindings2 = {
|
||||
'save-as-svg': this.activeArea.saveAsSvg.bind(this.activeArea),
|
||||
'save-as-json': this.activeArea.saveAsJson.bind(this.activeArea),
|
||||
'export-to-svg': this.activeArea.exportToSvg.bind(this.activeArea),
|
||||
'save-as-json': this.activeArea.saveAsJson.bind(this.activeArea, true, null),
|
||||
'open-previous-json': this.activeArea.loadPreviousJson.bind(this.activeArea),
|
||||
'open-next-json': this.activeArea.loadNextJson.bind(this.activeArea),
|
||||
'toggle-background': this.activeArea.toggleBackground.bind(this.activeArea),
|
||||
'toggle-grid': this.activeArea.toggleGrid.bind(this.activeArea),
|
||||
'toggle-square-area': this.activeArea.toggleSquareArea.bind(this.activeArea),
|
||||
'reverse-switch-font-family': this.activeArea.switchFontFamily.bind(this.activeArea, true),
|
||||
'switch-color-palette': this.activeArea.switchColorPalette.bind(this.activeArea, false),
|
||||
'switch-color-palette-reverse': this.activeArea.switchColorPalette.bind(this.activeArea, true),
|
||||
'switch-font-family': this.activeArea.switchFontFamily.bind(this.activeArea, false),
|
||||
'switch-font-family-reverse': this.activeArea.switchFontFamily.bind(this.activeArea, true),
|
||||
'switch-font-weight': this.activeArea.switchFontWeight.bind(this.activeArea),
|
||||
'switch-font-style': this.activeArea.switchFontStyle.bind(this.activeArea),
|
||||
'switch-text-alignment': this.activeArea.switchTextAlignment.bind(this.activeArea),
|
||||
'toggle-panel-and-dock-visibility': this.togglePanelAndDockOpacity.bind(this),
|
||||
'toggle-help': this.activeArea.toggleHelp.bind(this.activeArea),
|
||||
'open-user-stylesheet': this.openUserStyleFile.bind(this),
|
||||
'open-preferences': this.openPreferences.bind(this)
|
||||
};
|
||||
|
||||
for (let key in this.internalKeybindings1) {
|
||||
Main.wm.addKeybinding(key,
|
||||
this.settings,
|
||||
Me.internalShortcutSettings,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
DRAWING_ACTION_MODE,
|
||||
this.internalKeybindings1[key]);
|
||||
|
|
@ -236,7 +250,7 @@ var AreaManager = new Lang.Class({
|
|||
|
||||
for (let key in this.internalKeybindings2) {
|
||||
Main.wm.addKeybinding(key,
|
||||
this.settings,
|
||||
Me.internalShortcutSettings,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
DRAWING_ACTION_MODE | WRITING_ACTION_MODE,
|
||||
this.internalKeybindings2[key]);
|
||||
|
|
@ -245,10 +259,10 @@ var AreaManager = new Lang.Class({
|
|||
for (let i = 1; i < 10; i++) {
|
||||
let iCaptured = i;
|
||||
Main.wm.addKeybinding('select-color' + i,
|
||||
this.settings,
|
||||
Me.internalShortcutSettings,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
DRAWING_ACTION_MODE | WRITING_ACTION_MODE,
|
||||
() => this.activeArea.selectColor(iCaptured));
|
||||
this.activeArea.selectColor.bind(this.activeArea, iCaptured - 1));
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -272,27 +286,10 @@ var AreaManager = new Lang.Class({
|
|||
}
|
||||
},
|
||||
|
||||
openUserStyleFile: function() {
|
||||
if (!this.userStyleFile.query_exists(null)) {
|
||||
if (!this.userStyleFile.get_parent().query_exists(null))
|
||||
this.userStyleFile.get_parent().make_directory_with_parents(null);
|
||||
let defaultStyleFile = Me.dir.get_child('data').get_child('default.css');
|
||||
if (!defaultStyleFile.query_exists(null))
|
||||
return;
|
||||
let success = defaultStyleFile.copy(this.userStyleFile, Gio.FileCopyFlags.NONE, null, null);
|
||||
if (!success)
|
||||
return;
|
||||
}
|
||||
|
||||
Gio.AppInfo.launch_default_for_uri(this.userStyleFile.get_uri(), global.create_app_launch_context(0, -1));
|
||||
if (this.activeArea)
|
||||
this.toggleDrawing();
|
||||
},
|
||||
|
||||
eraseDrawing: function() {
|
||||
eraseDrawings: function() {
|
||||
for (let i = 0; i < this.areas.length; i++)
|
||||
this.areas[i].erase();
|
||||
if (this.settings.get_boolean('persistent-drawing'))
|
||||
if (this.persistentOverRestarts)
|
||||
this.areas[Main.layoutManager.primaryIndex].savePersistent();
|
||||
},
|
||||
|
||||
|
|
@ -345,7 +342,7 @@ var AreaManager = new Lang.Class({
|
|||
Main.uiGroup.set_child_at_index(Main.layoutManager.keyboardBox, this.oldKeyboardIndex);
|
||||
Main.uiGroup.remove_actor(activeContainer);
|
||||
Main.layoutManager._backgroundGroup.insert_child_above(activeContainer, Main.layoutManager._bgManagers[activeIndex].backgroundActor);
|
||||
if (!this.settings.get_boolean("drawing-on-desktop"))
|
||||
if (!this.onDesktop)
|
||||
activeContainer.hide();
|
||||
} else {
|
||||
Main.layoutManager._backgroundGroup.remove_actor(activeContainer);
|
||||
|
|
@ -365,8 +362,9 @@ var AreaManager = new Lang.Class({
|
|||
if (Main._findModal(this.activeArea) != -1) {
|
||||
Main.popModal(this.activeArea);
|
||||
if (source && source == global.display)
|
||||
this.showOsd(null, 'touchpad-disabled-symbolic', _("Keyboard and pointer released"), null, null, false);
|
||||
setCursor('DEFAULT');
|
||||
// Translators: "released" as the opposite of "grabbed"
|
||||
this.showOsd(null, Files.Icons.UNGRAB, _("Keyboard and pointer released"), null, null, false);
|
||||
this.setCursor('DEFAULT');
|
||||
this.activeArea.reactive = false;
|
||||
this.removeInternalKeybindings();
|
||||
} else {
|
||||
|
|
@ -378,7 +376,7 @@ var AreaManager = new Lang.Class({
|
|||
this.activeArea.reactive = true;
|
||||
this.activeArea.initPointerCursor();
|
||||
if (source && source == global.display)
|
||||
this.showOsd(null, 'input-touchpad-symbolic', _("Keyboard and pointer grabbed"), null, null, false);
|
||||
this.showOsd(null, Files.Icons.GRAB, _("Keyboard and pointer grabbed"), null, null, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -387,10 +385,11 @@ var AreaManager = new Lang.Class({
|
|||
toggleDrawing: function() {
|
||||
if (this.activeArea) {
|
||||
let activeIndex = this.areas.indexOf(this.activeArea);
|
||||
let save = activeIndex == Main.layoutManager.primaryIndex && this.settings.get_boolean('persistent-drawing');
|
||||
let save = activeIndex == Main.layoutManager.primaryIndex && this.persistentOverRestarts;
|
||||
let erase = !this.persistentOverToggles;
|
||||
|
||||
this.showOsd(null, this.leaveGicon, _("Leaving drawing mode"));
|
||||
this.activeArea.leaveDrawingMode(save);
|
||||
this.showOsd(null, Files.Icons.LEAVE, _("Leaving drawing mode"));
|
||||
this.activeArea.leaveDrawingMode(save, erase);
|
||||
if (this.hiddenList)
|
||||
this.togglePanelAndDockOpacity();
|
||||
|
||||
|
|
@ -410,9 +409,10 @@ var AreaManager = new Lang.Class({
|
|||
}
|
||||
|
||||
this.activeArea.enterDrawingMode();
|
||||
this.osdDisabled = this.settings.get_boolean('osd-disabled');
|
||||
let label = _("<small>Press <i>%s</i> for help</small>").format(this.activeArea.helper.helpKeyLabel) + "\n\n" + _("Entering drawing mode");
|
||||
this.showOsd(null, this.enterGicon, label, null, null, true);
|
||||
this.osdDisabled = Me.settings.get_boolean('osd-disabled');
|
||||
// Translators: %s is a key label
|
||||
let label = "<small>" + _("Press <i>%s</i> for help").format(this.activeArea.helper.helpKeyLabel) + "</small>\n\n" + _("Entering drawing mode");
|
||||
this.showOsd(null, Files.Icons.ENTER, label, null, null, true);
|
||||
}
|
||||
|
||||
if (this.indicator)
|
||||
|
|
@ -446,10 +446,8 @@ var AreaManager = new Lang.Class({
|
|||
if (level && GS_VERSION > '3.33.0')
|
||||
level = level / 100;
|
||||
|
||||
if (icon && typeof icon == 'string')
|
||||
icon = new Gio.ThemedIcon({ name: icon });
|
||||
else if (!icon)
|
||||
icon = this.enterGicon;
|
||||
if (!icon)
|
||||
icon = Files.Icons.ENTER;
|
||||
|
||||
let osdWindow = Main.osdWindowManager._osdWindows[activeIndex];
|
||||
|
||||
|
|
@ -503,13 +501,20 @@ var AreaManager = new Lang.Class({
|
|||
OsdWindow.HIDE_TIMEOUT = hideTimeoutSave;
|
||||
},
|
||||
|
||||
setCursor: function(cursorName) {
|
||||
// check display or screen (API changes)
|
||||
if (global.display.set_cursor)
|
||||
global.display.set_cursor(Meta.Cursor[cursorName]);
|
||||
else if (global.screen && global.screen.set_cursor)
|
||||
global.screen.set_cursor(Meta.Cursor[cursorName]);
|
||||
},
|
||||
|
||||
removeAreas: function() {
|
||||
for (let i = 0; i < this.areas.length; i++) {
|
||||
let area = this.areas[i];
|
||||
area.disconnect(area.leaveDrawingHandler);
|
||||
area.disconnect(area.updateActionModeHandler);
|
||||
area.disconnect(area.showOsdHandler);
|
||||
area.disconnect(area.showOsdGiconHandler);
|
||||
let container = area.get_parent();
|
||||
container.get_parent().remove_actor(container);
|
||||
container.destroy();
|
||||
|
|
@ -518,37 +523,35 @@ var AreaManager = new Lang.Class({
|
|||
},
|
||||
|
||||
disable: function() {
|
||||
if (this.userStyleHandler && this.userStyleMonitor) {
|
||||
this.userStyleMonitor.disconnect(this.userStyleHandler);
|
||||
this.userStyleHandler = null;
|
||||
}
|
||||
if (this.userStyleMonitor) {
|
||||
this.userStyleMonitor.cancel();
|
||||
this.userStyleMonitor = null;
|
||||
}
|
||||
if (this.monitorChangedHandler) {
|
||||
Main.layoutManager.disconnect(this.monitorChangedHandler);
|
||||
this.monitorChangedHandler = null;
|
||||
}
|
||||
if (this.indicatorSettingHandler) {
|
||||
this.settings.disconnect(this.indicatorSettingHandler);
|
||||
Me.settings.disconnect(this.indicatorSettingHandler);
|
||||
this.indicatorSettingHandler = null;
|
||||
}
|
||||
if (this.desktopSettingHandler) {
|
||||
this.settings.disconnect(this.desktopSettingHandler);
|
||||
Me.settings.disconnect(this.desktopSettingHandler);
|
||||
this.desktopSettingHandler = null;
|
||||
}
|
||||
if (this.persistentSettingHandler) {
|
||||
this.settings.disconnect(this.persistentSettingHandler);
|
||||
this.persistentSettingHandler = null;
|
||||
if (this.persistentOverTogglesSettingHandler) {
|
||||
Me.settings.disconnect(this.persistentOverTogglesSettingHandler);
|
||||
this.persistentOverTogglesSettingHandler = null;
|
||||
}
|
||||
if (this.persistentOverRestartsSettingHandler) {
|
||||
Me.settings.disconnect(this.persistentOverRestartsSettingHandler);
|
||||
this.persistentOverRestartsSettingHandler = null;
|
||||
}
|
||||
|
||||
if (this.activeArea)
|
||||
this.toggleDrawing();
|
||||
Main.wm.removeKeybinding('toggle-drawing');
|
||||
Main.wm.removeKeybinding('toggle-modal');
|
||||
Main.wm.removeKeybinding('erase-drawing');
|
||||
Main.wm.removeKeybinding('erase-drawings');
|
||||
this.removeAreas();
|
||||
Files.Images.disable();
|
||||
Files.Jsons.disable();
|
||||
if (this.indicator)
|
||||
this.indicator.disable();
|
||||
}
|
||||
|
|
@ -601,12 +604,3 @@ const DrawingIndicator = new Lang.Class({
|
|||
}
|
||||
});
|
||||
|
||||
function setCursor(cursorName) {
|
||||
// check display or screen (API changes)
|
||||
if (global.display.set_cursor)
|
||||
global.display.set_cursor(Meta.Cursor[cursorName]);
|
||||
else if (global.screen && global.screen.set_cursor)
|
||||
global.screen.set_cursor(Meta.Cursor[cursorName]);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
486
files.js
|
|
@ -1,4 +1,5 @@
|
|||
/* jslint esversion: 6 */
|
||||
/* exported Icons, Image, Images, Json, Jsons, getDateString, saveSvg */
|
||||
|
||||
/*
|
||||
* Copyright 2019 Abakkk
|
||||
|
|
@ -26,13 +27,54 @@ const GdkPixbuf = imports.gi.GdkPixbuf;
|
|||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const EXAMPLE_IMAGES = Me.dir.get_child('data').get_child('images');
|
||||
const USER_IMAGES = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir'], 'images']));
|
||||
const EXAMPLE_IMAGE_DIRECTORY = Me.dir.get_child('data').get_child('images');
|
||||
const DEFAULT_USER_IMAGE_LOCATION = GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir'], 'images']);
|
||||
const Clipboard = St.Clipboard.get_default();
|
||||
const CLIPBOARD_TYPE = St.ClipboardType.CLIPBOARD;
|
||||
const ICON_DIR = Me.dir.get_child('data').get_child('icons');
|
||||
const ICON_NAMES = [
|
||||
'arc', 'color', 'dashed-line', 'document-export', 'fillrule-evenodd', 'fillrule-nonzero', 'fill', 'full-line', 'linecap', 'linejoin', 'palette', 'smooth', 'stroke',
|
||||
'tool-ellipse', 'tool-line', 'tool-mirror', 'tool-move', 'tool-none', 'tool-polygon', 'tool-polyline', 'tool-rectangle', 'tool-resize',
|
||||
];
|
||||
const ThemedIconNames = {
|
||||
ENTER: 'applications-graphics', LEAVE: 'application-exit',
|
||||
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',
|
||||
TOOL_IMAGE: 'insert-image', TOOL_TEXT: 'insert-text',
|
||||
};
|
||||
|
||||
// wrapper around an image file
|
||||
var Icons = {};
|
||||
|
||||
ICON_NAMES.forEach(name => {
|
||||
Object.defineProperty(Icons, name.toUpperCase().replace(/-/gi, '_'), {
|
||||
get: function() {
|
||||
if (!this[`_${name}`]) {
|
||||
let file = Gio.File.new_for_path(ICON_DIR.get_child(`${name}-symbolic.svg`).get_path());
|
||||
this[`_${name}`] = file.query_exists(null) ? new Gio.FileIcon({ file }) : new Gio.ThemedIcon({ name: 'action-unavailable-symbolic' });
|
||||
}
|
||||
return this[`_${name}`];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Object.keys(ThemedIconNames).forEach(key => {
|
||||
Object.defineProperty(Icons, key, {
|
||||
get: function() {
|
||||
if (!this[`_${key}`])
|
||||
this[`_${key}`] = new Gio.ThemedIcon({ name: `${ThemedIconNames[key]}-symbolic` });
|
||||
return this[`_${key}`];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Wrapper around image data. If not subclassed, it is used when loading in the area an image element for a drawing file (.json)
|
||||
// and it takes { displayName, contentType, base64, hash } as params.
|
||||
var Image = new Lang.Class({
|
||||
Name: 'DrawOnYourScreenImage',
|
||||
|
||||
|
|
@ -54,29 +96,9 @@ var Image = new Lang.Class({
|
|||
};
|
||||
},
|
||||
|
||||
// only called from menu so file exists
|
||||
get gicon() {
|
||||
if (!this._gicon)
|
||||
this._gicon = new Gio.FileIcon({ file: this.file });
|
||||
return this._gicon;
|
||||
},
|
||||
|
||||
get bytes() {
|
||||
if (!this._bytes) {
|
||||
if (this.file)
|
||||
try {
|
||||
// load_bytes available in GLib 2.56+
|
||||
this._bytes = this.file.load_bytes(null)[0];
|
||||
} catch(e) {
|
||||
let [success_, contents] = this.file.load_contents(null);
|
||||
if (contents instanceof Uint8Array)
|
||||
this._bytes = ByteArray.toGBytes(contents);
|
||||
else
|
||||
this._bytes = contents.toGBytes();
|
||||
}
|
||||
else
|
||||
this._bytes = new GLib.Bytes(GLib.base64_decode(this.base64));
|
||||
}
|
||||
if (!this._bytes)
|
||||
this._bytes = new GLib.Bytes(GLib.base64_decode(this.base64));
|
||||
return this._bytes;
|
||||
},
|
||||
|
||||
|
|
@ -124,34 +146,207 @@ var Image = new Lang.Class({
|
|||
}
|
||||
});
|
||||
|
||||
var getImages = function() {
|
||||
let images = [];
|
||||
// Add a gicon generator to Image. It is used with image files and it takes { file, info } as params.
|
||||
const ImageWithGicon = new Lang.Class({
|
||||
Name: 'DrawOnYourScreenImageWithGicon',
|
||||
Extends: Image,
|
||||
|
||||
[EXAMPLE_IMAGES, USER_IMAGES].forEach(directory => {
|
||||
let enumerator;
|
||||
try {
|
||||
enumerator = directory.enumerate_children('standard::display-name,standard::content-type', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch(e) {
|
||||
return;
|
||||
get displayName() {
|
||||
return this.info.get_display_name();
|
||||
},
|
||||
|
||||
get contentType() {
|
||||
return this.info.get_content_type();
|
||||
},
|
||||
|
||||
get thumbnailFile() {
|
||||
if (!this._thumbnailFile) {
|
||||
if (this.info.has_attribute('thumbnail::path') && this.info.get_attribute_boolean('thumbnail::is-valid')) {
|
||||
let thumbnailPath = this.info.get_attribute_as_string('thumbnail::path');
|
||||
this._thumbnailFile = Gio.File.new_for_path(thumbnailPath);
|
||||
}
|
||||
}
|
||||
return this._thumbnailFile || null;
|
||||
},
|
||||
|
||||
get gicon() {
|
||||
if (!this._gicon)
|
||||
this._gicon = new Gio.FileIcon({ file: this.thumbnailFile || this.file });
|
||||
return this._gicon;
|
||||
},
|
||||
|
||||
// use only thumbnails in menu (memory)
|
||||
get thumbnailGicon() {
|
||||
if (this.contentType != 'image/svg+xml' && !this.thumbnailFile)
|
||||
return null;
|
||||
|
||||
let fileInfo = enumerator.next_file(null);
|
||||
while (fileInfo) {
|
||||
if (fileInfo.get_content_type().indexOf('image') == 0)
|
||||
images.push(new Image({ file: enumerator.get_child(fileInfo), contentType: fileInfo.get_content_type(), displayName: fileInfo.get_display_name() }));
|
||||
fileInfo = enumerator.next_file(null);
|
||||
return this.gicon;
|
||||
},
|
||||
|
||||
get bytes() {
|
||||
if (!this._bytes) {
|
||||
try {
|
||||
// load_bytes available in GLib 2.56+
|
||||
this._bytes = this.file.load_bytes(null)[0];
|
||||
} catch(e) {
|
||||
let [, contents] = this.file.load_contents(null);
|
||||
if (contents instanceof Uint8Array)
|
||||
this._bytes = ByteArray.toGBytes(contents);
|
||||
else
|
||||
this._bytes = contents.toGBytes();
|
||||
}
|
||||
}
|
||||
enumerator.close(null);
|
||||
});
|
||||
return this._bytes;
|
||||
}
|
||||
});
|
||||
|
||||
// It is directly generated from a Json object, without an image file. It takes { bytes, displayName, gicon } as params.
|
||||
const ImageFromJson = new Lang.Class({
|
||||
Name: 'DrawOnYourScreenImageFromJson',
|
||||
Extends: Image,
|
||||
contentType: 'image/svg+xml',
|
||||
|
||||
images.sort((a, b) => {
|
||||
return a.displayName.localeCompare(b.displayName);
|
||||
});
|
||||
get bytes() {
|
||||
return this._bytes;
|
||||
},
|
||||
|
||||
return images;
|
||||
set bytes(bytes) {
|
||||
this._bytes = bytes;
|
||||
}
|
||||
});
|
||||
|
||||
// Access images with getPrevious, getNext, getSorted or by iterating over it.
|
||||
var Images = {
|
||||
_images: [],
|
||||
_clipboardImages: [],
|
||||
_upToDate: false,
|
||||
|
||||
disable: function() {
|
||||
this._images = [];
|
||||
this._clipboardImages = [];
|
||||
this._upToDate = false;
|
||||
},
|
||||
|
||||
_clipboardImagesContains: function(file) {
|
||||
return this._clipboardImages.some(image => image.file.equal(file));
|
||||
},
|
||||
|
||||
// Firstly iterate over the extension directory that contains Example.svg,
|
||||
// secondly iterate over the directory that was configured by the user in prefs,
|
||||
// finally iterate over the images pasted from the clipboard.
|
||||
[Symbol.iterator]: function() {
|
||||
if (this._upToDate)
|
||||
return this._images.concat(this._clipboardImages)[Symbol.iterator]();
|
||||
|
||||
this._upToDate = true;
|
||||
let oldImages = this._images;
|
||||
let newImages = this._images = [];
|
||||
let clipboardImagesContains = this._clipboardImagesContains.bind(this);
|
||||
let clipboardIterator = this._clipboardImages[Symbol.iterator]();
|
||||
|
||||
return {
|
||||
getExampleEnumerator: function() {
|
||||
try {
|
||||
return EXAMPLE_IMAGE_DIRECTORY.enumerate_children('standard::,thumbnail::', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch(e) {
|
||||
return this.getUserEnumerator();
|
||||
}
|
||||
},
|
||||
|
||||
getUserEnumerator: function() {
|
||||
try {
|
||||
let userLocation = Me.drawingSettings.get_string('image-location') || DEFAULT_USER_IMAGE_LOCATION;
|
||||
let userDirectory = Gio.File.new_for_commandline_arg(userLocation);
|
||||
return userDirectory.enumerate_children('standard::,thumbnail::', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch(e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
get enumerator() {
|
||||
if (this._enumerator === undefined)
|
||||
this._enumerator = this.getExampleEnumerator();
|
||||
else if (this._enumerator && this._enumerator.get_container().equal(EXAMPLE_IMAGE_DIRECTORY) && this._enumerator.is_closed())
|
||||
this._enumerator = this.getUserEnumerator();
|
||||
else if (this._enumerator && this._enumerator.is_closed())
|
||||
this._enumerator = null;
|
||||
|
||||
return this._enumerator;
|
||||
},
|
||||
|
||||
next: function() {
|
||||
if (!this.enumerator)
|
||||
return clipboardIterator.next();
|
||||
|
||||
let info = this.enumerator.next_file(null);
|
||||
if (!info) {
|
||||
this.enumerator.close(null);
|
||||
return this.next();
|
||||
}
|
||||
|
||||
let file = this.enumerator.get_child(info);
|
||||
|
||||
if (info.get_content_type().indexOf('image') == 0 && !clipboardImagesContains(file)) {
|
||||
let image = oldImages.find(oldImage => oldImage.file.equal(file)) || new ImageWithGicon({ file, info });
|
||||
newImages.push(image);
|
||||
return { value: image, done: false };
|
||||
} else {
|
||||
return this.next();
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getSorted: function() {
|
||||
return [...this].sort((a, b) => a.toString().localeCompare(b.toString()));
|
||||
},
|
||||
|
||||
getNext: function(currentImage) {
|
||||
let images = this.getSorted();
|
||||
let index = currentImage && currentImage.file ? images.findIndex(image => image.file.equal(currentImage.file)) : -1;
|
||||
return images[index == images.length - 1 ? 0 : index + 1] || null;
|
||||
},
|
||||
|
||||
getPrevious: function(currentImage) {
|
||||
let images = this.getSorted();
|
||||
let index = currentImage && currentImage.file ? images.findIndex(image => image.file.equal(currentImage.file)) : -1;
|
||||
return images[index <= 0 ? images.length - 1 : index - 1] || null;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._upToDate = false;
|
||||
},
|
||||
|
||||
addImagesFromClipboard: function(callback) {
|
||||
Clipboard.get_text(CLIPBOARD_TYPE, (clipBoard, text) => {
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
let lines = text.split('\n');
|
||||
if (lines[0] == 'x-special/nautilus-clipboard')
|
||||
lines = lines.slice(2);
|
||||
|
||||
let images = lines.filter(line => !!line)
|
||||
.map(line => Gio.File.new_for_commandline_arg(line))
|
||||
.filter(file => file.query_exists(null))
|
||||
.map(file => [file, file.query_info('standard::,thumbnail::', Gio.FileQueryInfoFlags.NONE, null)])
|
||||
.filter(pair => pair[1].get_content_type().indexOf('image') == 0)
|
||||
.map(pair => new ImageWithGicon({ file: pair[0], info: pair[1] }));
|
||||
|
||||
// Prevent duplicated
|
||||
images.filter(image => !this._clipboardImagesContains(image.file))
|
||||
.forEach(image => this._clipboardImages.push(image));
|
||||
|
||||
if (images.length) {
|
||||
this.reset();
|
||||
let lastFile = images[images.length - 1].file;
|
||||
callback(this._clipboardImages.find(image => image.file.equal(lastFile)));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// wrapper around a json file
|
||||
// Wrapper around a json file (drawing saves).
|
||||
var Json = new Lang.Class({
|
||||
Name: 'DrawOnYourScreenJson',
|
||||
|
||||
|
|
@ -160,6 +355,10 @@ var Json = new Lang.Class({
|
|||
this[key] = params[key];
|
||||
},
|
||||
|
||||
get isPersistent() {
|
||||
return this.name == Me.metadata['persistent-file-name'];
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return this.displayName || this.name;
|
||||
},
|
||||
|
|
@ -169,10 +368,10 @@ var Json = new Lang.Class({
|
|||
},
|
||||
|
||||
get file() {
|
||||
if (!this._file && this.name)
|
||||
if (!this._file)
|
||||
this._file = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir'], `${this.name}.json`]));
|
||||
|
||||
return this._file || null;
|
||||
return this._file;
|
||||
},
|
||||
|
||||
set file(file) {
|
||||
|
|
@ -180,62 +379,181 @@ var Json = new Lang.Class({
|
|||
},
|
||||
|
||||
get contents() {
|
||||
let success_, contents;
|
||||
try {
|
||||
[success_, contents] = this.file.load_contents(null);
|
||||
if (contents instanceof Uint8Array)
|
||||
contents = ByteArray.toString(contents);
|
||||
} catch(e) {
|
||||
return null;
|
||||
if (this._contents === undefined) {
|
||||
try {
|
||||
[, this._contents] = this.file.load_contents(null);
|
||||
if (this._contents instanceof Uint8Array)
|
||||
this._contents = ByteArray.toString(this._contents);
|
||||
} catch(e) {
|
||||
this._contents = null;
|
||||
}
|
||||
}
|
||||
return contents;
|
||||
|
||||
return this._contents;
|
||||
},
|
||||
|
||||
set contents(contents) {
|
||||
if (this.isPersistent && (this.contents == contents || !this.contents && contents == '[]'))
|
||||
return;
|
||||
|
||||
try {
|
||||
this.file.replace_contents(contents, null, false, Gio.FileCreateFlags.NONE, null);
|
||||
} catch(e) {
|
||||
this.file.get_parent().make_directory_with_parents(null);
|
||||
this.file.replace_contents(contents, null, false, Gio.FileCreateFlags.NONE, null);
|
||||
}
|
||||
|
||||
this._contents = contents;
|
||||
},
|
||||
|
||||
addSvgContents: function(getGiconSvgContent, getImageSvgContent) {
|
||||
let giconSvgBytes = new GLib.Bytes(getGiconSvgContent());
|
||||
this.gicon = Gio.BytesIcon.new(giconSvgBytes);
|
||||
this.getImageSvgBytes = () => new GLib.Bytes(getImageSvgContent());
|
||||
},
|
||||
|
||||
get image() {
|
||||
if (!this._image)
|
||||
this._image = new ImageFromJson({ bytes: this.getImageSvgBytes(), gicon: this.gicon, displayName: this.displayName });
|
||||
|
||||
return this._image;
|
||||
}
|
||||
});
|
||||
|
||||
var getJsons = function() {
|
||||
let directory = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir']]));
|
||||
// Access jsons with getPersistent, getDated, getNamed, getPrevious, getNext, getSorted or by iterating over it.
|
||||
var Jsons = {
|
||||
_jsons: [],
|
||||
_upToDate: false,
|
||||
|
||||
let enumerator;
|
||||
try {
|
||||
enumerator = directory.enumerate_children('standard::name,standard::display-name,standard::content-type,time::modified', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch(e) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let jsons = [];
|
||||
let fileInfo = enumerator.next_file(null);
|
||||
while (fileInfo) {
|
||||
if (fileInfo.get_content_type().indexOf('json') != -1 && fileInfo.get_name() != `${Me.metadata['persistent-file-name']}.json`) {
|
||||
let file = enumerator.get_child(fileInfo);
|
||||
jsons.push(new Json({
|
||||
file,
|
||||
name: fileInfo.get_name().slice(0, -5),
|
||||
displayName: fileInfo.get_display_name().slice(0, -5),
|
||||
// fileInfo.get_modification_date_time: Gio 2.62+
|
||||
modificationUnixTime: fileInfo.get_attribute_uint64('time::modified')
|
||||
}));
|
||||
disable: function() {
|
||||
if (this._monitor) {
|
||||
this._monitor.disconnect(this._monitorHandler);
|
||||
this._monitor.cancel();
|
||||
}
|
||||
fileInfo = enumerator.next_file(null);
|
||||
|
||||
delete this._monitor;
|
||||
delete this._persistent;
|
||||
|
||||
this._jsons = [];
|
||||
this._upToDate = false;
|
||||
},
|
||||
|
||||
_updateMonitor: function() {
|
||||
if (this._monitor)
|
||||
return;
|
||||
|
||||
let directory = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir']]));
|
||||
this._monitor = directory.monitor(Gio.FileMonitorFlags.NONE, null);
|
||||
this._monitorHandler = this._monitor.connect('changed', (monitor, file) => {
|
||||
if (file.get_basename() != `${Me.metadata['persistent-file-name']}.json` && file.get_basename().indexOf('.goutputstream'))
|
||||
this.reset();
|
||||
});
|
||||
},
|
||||
|
||||
[Symbol.iterator]: function() {
|
||||
if (this._upToDate)
|
||||
return this._jsons[Symbol.iterator]();
|
||||
|
||||
this._updateMonitor();
|
||||
this._upToDate = true;
|
||||
let newJsons = this._jsons = [];
|
||||
|
||||
return {
|
||||
get enumerator() {
|
||||
if (this._enumerator === undefined) {
|
||||
try {
|
||||
let directory = Gio.File.new_for_path(GLib.build_filenamev([GLib.get_user_data_dir(), Me.metadata['data-dir']]));
|
||||
this._enumerator = directory.enumerate_children('standard::name,standard::display-name,standard::content-type,time::modified', Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch(e) {
|
||||
this._enumerator = null;
|
||||
}
|
||||
}
|
||||
|
||||
return this._enumerator;
|
||||
},
|
||||
|
||||
next: function() {
|
||||
if (!this.enumerator || this.enumerator.is_closed())
|
||||
return { done: true };
|
||||
|
||||
let info = this.enumerator.next_file(null);
|
||||
if (!info) {
|
||||
this.enumerator.close(null);
|
||||
return this.next();
|
||||
}
|
||||
|
||||
let file = this.enumerator.get_child(info);
|
||||
|
||||
if (info.get_content_type().indexOf('json') != -1 && info.get_name() != `${Me.metadata['persistent-file-name']}.json`) {
|
||||
let json = new Json({
|
||||
file, name: info.get_name().slice(0, -5),
|
||||
displayName: info.get_display_name().slice(0, -5),
|
||||
// info.get_modification_date_time: Gio 2.62+
|
||||
modificationUnixTime: info.get_attribute_uint64('time::modified')
|
||||
});
|
||||
|
||||
newJsons.push(json);
|
||||
return { value: json, done: false };
|
||||
} else {
|
||||
return this.next();
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getSorted: function() {
|
||||
return [...this].sort((a, b) => b.modificationUnixTime - a.modificationUnixTime);
|
||||
},
|
||||
|
||||
getNext: function(currentJson) {
|
||||
let jsons = this.getSorted();
|
||||
let index = currentJson ? jsons.findIndex(json => json.name == currentJson.name) : -1;
|
||||
return jsons[index == jsons.length - 1 ? 0 : index + 1] || null;
|
||||
},
|
||||
|
||||
getPrevious: function(currentJson) {
|
||||
let jsons = this.getSorted();
|
||||
let index = currentJson ? jsons.findIndex(json => json.name == currentJson.name) : -1;
|
||||
return jsons[index <= 0 ? jsons.length - 1 : index - 1] || null;
|
||||
},
|
||||
|
||||
getPersistent: function() {
|
||||
if (!this._persistent)
|
||||
this._persistent = new Json({ name: Me.metadata['persistent-file-name'] });
|
||||
|
||||
return this._persistent;
|
||||
},
|
||||
|
||||
getDated: function() {
|
||||
return new Json({ name: getDateString() });
|
||||
},
|
||||
|
||||
getNamed: function(name) {
|
||||
return [...this].find(json => json.name == name) || new Json({ name });
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this._upToDate = false;
|
||||
}
|
||||
enumerator.close(null);
|
||||
|
||||
jsons.sort((a, b) => {
|
||||
return b.modificationUnixTime - a.modificationUnixTime;
|
||||
});
|
||||
|
||||
return jsons;
|
||||
};
|
||||
|
||||
var getDateString = function() {
|
||||
let date = GLib.DateTime.new_now_local();
|
||||
return `${date.format("%F")} ${date.format("%X")}`;
|
||||
};
|
||||
|
||||
var saveSvg = function(content) {
|
||||
let filename = `${Me.metadata['svg-file-name']} ${getDateString()}.svg`;
|
||||
let dir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES);
|
||||
let path = GLib.build_filenamev([dir, filename]);
|
||||
let file = Gio.File.new_for_path(path);
|
||||
if (file.query_exists(null))
|
||||
return false;
|
||||
|
||||
try {
|
||||
return file.replace_contents(content, null, false, Gio.FileCreateFlags.NONE, null)[0];
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
110
helper.js
|
|
@ -1,4 +1,5 @@
|
|||
/* jslint esversion: 6 */
|
||||
/* exported DrawingHelper */
|
||||
|
||||
/*
|
||||
* Copyright 2019 Abakkk
|
||||
|
|
@ -25,24 +26,19 @@ const Lang = imports.lang;
|
|||
const St = imports.gi.St;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = ExtensionUtils.getSettings ? ExtensionUtils : Me.imports.convenience;
|
||||
const Prefs = Me.imports.prefs;
|
||||
const Shortcuts = Me.imports.shortcuts;
|
||||
const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext;
|
||||
|
||||
const GS_VERSION = Config.PACKAGE_VERSION;
|
||||
|
||||
const HELPER_ANIMATION_TIME = 0.25;
|
||||
const MEDIA_KEYS_SCHEMA = 'org.gnome.settings-daemon.plugins.media-keys';
|
||||
const MEDIA_KEYS_KEYS = {
|
||||
'screenshot': "Screenshot",
|
||||
'screenshot-clip': "Screenshot to clipboard",
|
||||
'area-screenshot': "Area screenshot",
|
||||
'area-screenshot-clip': "Area screenshot to clipboard"
|
||||
};
|
||||
const MEDIA_KEYS_KEYS = ['screenshot', 'screenshot-clip', 'area-screenshot', 'area-screenshot-clip'];
|
||||
|
||||
// DrawingHelper provides the "help osd" (Ctrl + F1)
|
||||
// It uses the same texts as in prefs
|
||||
|
|
@ -55,24 +51,27 @@ var DrawingHelper = new Lang.Class({
|
|||
this.parent(params);
|
||||
this.monitor = monitor;
|
||||
this.hide();
|
||||
this.settings = Convenience.getSettings();
|
||||
|
||||
this.settingHandler = this.settings.connect('changed', this._onSettingChanged.bind(this));
|
||||
this.connect('destroy', () => this.settings.disconnect(this.settingHandler));
|
||||
this.settingsHandler = Me.settings.connect('changed', this._onSettingsChanged.bind(this));
|
||||
this.internalShortcutsettingsHandler = Me.internalShortcutSettings.connect('changed', this._onSettingsChanged.bind(this));
|
||||
this.connect('destroy', () => {
|
||||
Me.settings.disconnect(this.settingsHandler);
|
||||
Me.internalShortcutSettings.disconnect(this.internalShortcutsettingsHandler);
|
||||
});
|
||||
},
|
||||
|
||||
_onSettingChanged: function(settings, key) {
|
||||
_onSettingsChanged: function(settings, key) {
|
||||
if (key == 'toggle-help')
|
||||
this._updateHelpKeyLabel();
|
||||
|
||||
if (this.vbox) {
|
||||
this.vbox.destroy();
|
||||
this.vbox = null;
|
||||
delete this.vbox;
|
||||
}
|
||||
},
|
||||
|
||||
_updateHelpKeyLabel: function() {
|
||||
let [keyval, mods] = Gtk.accelerator_parse(this.settings.get_strv('toggle-help')[0]);
|
||||
let [keyval, mods] = Gtk.accelerator_parse(Me.internalShortcutSettings.get_strv('toggle-help')[0] || '');
|
||||
this._helpKeyLabel = Gtk.accelerator_get_label(keyval, mods);
|
||||
},
|
||||
|
||||
|
|
@ -88,63 +87,72 @@ var DrawingHelper = new Lang.Class({
|
|||
this.add_actor(this.vbox);
|
||||
this.vbox.add_child(new St.Label({ text: _("Global") }));
|
||||
|
||||
for (let settingKey in Prefs.GLOBAL_KEYBINDINGS) {
|
||||
let hbox = new St.BoxLayout({ vertical: false });
|
||||
if (settingKey.indexOf('-separator-') != -1) {
|
||||
Shortcuts.GLOBAL_KEYBINDINGS.forEach((settingKeys, index) => {
|
||||
if (index)
|
||||
this.vbox.add_child(new St.BoxLayout({ vertical: false, style_class: 'draw-on-your-screen-helper-separator' }));
|
||||
|
||||
settingKeys.forEach(settingKey => {
|
||||
if (!Me.settings.get_strv(settingKey)[0])
|
||||
return;
|
||||
|
||||
let hbox = new St.BoxLayout({ vertical: false });
|
||||
let [keyval, mods] = Gtk.accelerator_parse(Me.settings.get_strv(settingKey)[0] || '');
|
||||
hbox.add_child(new St.Label({ text: Me.settings.settings_schema.get_key(settingKey).get_summary() }));
|
||||
hbox.add_child(new St.Label({ text: Gtk.accelerator_get_label(keyval, mods), x_expand: true }));
|
||||
this.vbox.add_child(hbox);
|
||||
continue;
|
||||
}
|
||||
if (!this.settings.get_strv(settingKey)[0])
|
||||
continue;
|
||||
let [keyval, mods] = Gtk.accelerator_parse(this.settings.get_strv(settingKey)[0]);
|
||||
hbox.add_child(new St.Label({ text: _(Prefs.GLOBAL_KEYBINDINGS[settingKey]) }));
|
||||
hbox.add_child(new St.Label({ text: Gtk.accelerator_get_label(keyval, mods), x_expand: true }));
|
||||
this.vbox.add_child(hbox);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.vbox.add_child(new St.BoxLayout({ vertical: false, style_class: 'draw-on-your-screen-helper-separator' }));
|
||||
this.vbox.add_child(new St.Label({ text: _("Internal") }));
|
||||
|
||||
for (let i = 0; i < Prefs.OTHER_SHORTCUTS.length; i++) {
|
||||
if (Prefs.OTHER_SHORTCUTS[i].desc.indexOf('-separator-') != -1) {
|
||||
Shortcuts.OTHERS.forEach((pairs, index) => {
|
||||
if (index)
|
||||
this.vbox.add_child(new St.BoxLayout({ vertical: false, style_class: 'draw-on-your-screen-helper-separator' }));
|
||||
continue;
|
||||
}
|
||||
let hbox = new St.BoxLayout({ vertical: false });
|
||||
hbox.add_child(new St.Label({ text: _(Prefs.OTHER_SHORTCUTS[i].desc) }));
|
||||
hbox.add_child(new St.Label({ text: Prefs.OTHER_SHORTCUTS[i].shortcut, x_expand: true }));
|
||||
hbox.get_children()[0].get_clutter_text().set_use_markup(true);
|
||||
this.vbox.add_child(hbox);
|
||||
}
|
||||
|
||||
pairs.forEach(pair => {
|
||||
let [action, shortcut] = pair;
|
||||
let hbox = new St.BoxLayout({ vertical: false });
|
||||
hbox.add_child(new St.Label({ text: action }));
|
||||
hbox.add_child(new St.Label({ text: shortcut, x_expand: true }));
|
||||
hbox.get_children()[0].get_clutter_text().set_use_markup(true);
|
||||
this.vbox.add_child(hbox);
|
||||
});
|
||||
});
|
||||
|
||||
this.vbox.add_child(new St.BoxLayout({ vertical: false, style_class: 'draw-on-your-screen-helper-separator' }));
|
||||
|
||||
for (let settingKey in Prefs.INTERNAL_KEYBINDINGS) {
|
||||
if (settingKey.indexOf('-separator-') != -1) {
|
||||
Shortcuts.INTERNAL_KEYBINDINGS.forEach((settingKeys, index) => {
|
||||
if (index)
|
||||
this.vbox.add_child(new St.BoxLayout({ vertical: false, style_class: 'draw-on-your-screen-helper-separator' }));
|
||||
continue;
|
||||
}
|
||||
let hbox = new St.BoxLayout({ vertical: false });
|
||||
if (!this.settings.get_strv(settingKey)[0])
|
||||
continue;
|
||||
let [keyval, mods] = Gtk.accelerator_parse(this.settings.get_strv(settingKey)[0]);
|
||||
hbox.add_child(new St.Label({ text: _(Prefs.INTERNAL_KEYBINDINGS[settingKey]) }));
|
||||
hbox.add_child(new St.Label({ text: Gtk.accelerator_get_label(keyval, mods), x_expand: true }));
|
||||
this.vbox.add_child(hbox);
|
||||
}
|
||||
|
||||
settingKeys.forEach(settingKey => {
|
||||
if (!Me.internalShortcutSettings.get_strv(settingKey)[0])
|
||||
return;
|
||||
|
||||
let hbox = new St.BoxLayout({ vertical: false });
|
||||
let [keyval, mods] = Gtk.accelerator_parse(Me.internalShortcutSettings.get_strv(settingKey)[0] || '');
|
||||
hbox.add_child(new St.Label({ text: Me.internalShortcutSettings.settings_schema.get_key(settingKey).get_summary() }));
|
||||
hbox.add_child(new St.Label({ text: Gtk.accelerator_get_label(keyval, mods), x_expand: true }));
|
||||
this.vbox.add_child(hbox);
|
||||
});
|
||||
});
|
||||
|
||||
let mediaKeysSettings;
|
||||
try { mediaKeysSettings = Convenience.getSettings(MEDIA_KEYS_SCHEMA); } catch(e) { return; }
|
||||
|
||||
this.vbox.add_child(new St.BoxLayout({ vertical: false, style_class: 'draw-on-your-screen-helper-separator' }));
|
||||
this.vbox.add_child(new St.Label({ text: _("System") }));
|
||||
|
||||
for (let settingKey in MEDIA_KEYS_KEYS) {
|
||||
for (let settingKey of MEDIA_KEYS_KEYS) {
|
||||
if (!mediaKeysSettings.settings_schema.has_key(settingKey))
|
||||
continue;
|
||||
let shortcut = GS_VERSION < '3.33.0' ? mediaKeysSettings.get_string(settingKey) : mediaKeysSettings.get_strv(settingKey)[0];
|
||||
if (!shortcut)
|
||||
continue;
|
||||
let [keyval, mods] = Gtk.accelerator_parse(shortcut);
|
||||
let [keyval, mods] = Gtk.accelerator_parse(shortcut || '');
|
||||
let hbox = new St.BoxLayout({ vertical: false });
|
||||
hbox.add_child(new St.Label({ text: _(MEDIA_KEYS_KEYS[settingKey]) }));
|
||||
hbox.add_child(new St.Label({ text: mediaKeysSettings.settings_schema.get_key(settingKey).get_summary() }));
|
||||
hbox.add_child(new St.Label({ text: Gtk.accelerator_get_label(keyval, mods), x_expand: true }));
|
||||
this.vbox.add_child(hbox);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# xgettext --from-code=UTF-8 --add-comments="Translators: " --no-location --package-name="Draw On Your Screen" --msgid-bugs-address="https://framagit.org/abakkk/DrawOnYourScreen/issues" -f locale/POTFILES.in
|
||||
area.js
|
||||
extension.js
|
||||
helper.js
|
||||
menu.js
|
||||
prefs.js
|
||||
shortcuts.js
|
||||
schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml
|
||||
|
|
@ -8,9 +8,9 @@
|
|||
# You are free to translate them or not.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Draw On Your Screen VERSION\n"
|
||||
"Project-Id-Version: Draw On Your Screen\n"
|
||||
"Report-Msgid-Bugs-To: https://framagit.org/abakkk/DrawOnYourScreen/issues\n"
|
||||
"POT-Creation-Date: 2019-03-04 16:40+0100\n"
|
||||
"POT-Creation-Date: 2020-09-17 22:27+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
|
@ -19,66 +19,344 @@ msgstr ""
|
|||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "About"
|
||||
#. Translators: %s is a key label
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Press <i>%s</i> to get\n"
|
||||
"a fourth control point"
|
||||
msgstr ""
|
||||
|
||||
# You are free to translate the extension name, that is displayed in About page, or not.
|
||||
msgid "Draw On You Screen"
|
||||
msgid "Mark a point of symmetry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Version %d"
|
||||
msgid "Draw a line of symmetry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start drawing with Super+Alt+D and save your beautiful work by taking a screenshot"
|
||||
#. Translators: initial content of the text area
|
||||
msgctxt "text-area-content"
|
||||
msgid "Text"
|
||||
msgstr ""
|
||||
|
||||
# Add your name here, for example:
|
||||
# (add "\n" as separator if there is many translators)
|
||||
# msgid "translator-credits"
|
||||
# msgstr "Me"
|
||||
# or, with mail:
|
||||
# msgid "translator-credits"
|
||||
# msgstr "<a href=\"mailto:me@mail.org\">Me</a>"
|
||||
# or, with page:
|
||||
# msgid "translator-credits"
|
||||
# msgstr "<a href=\"https://...\">Me</a>"
|
||||
# else keep it empty.
|
||||
# It will be displayed in about page
|
||||
msgid "translator-credits"
|
||||
#. Translators: %s is a key label
|
||||
#, javascript-format
|
||||
msgid "Press <i>%s</i> to mark vertices"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preferences"
|
||||
#. Translators: %s is a key label
|
||||
#, javascript-format
|
||||
msgid "Type your text and press <i>%s</i>"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: "released" as the opposite of "grabbed"
|
||||
msgid "Keyboard and pointer released"
|
||||
msgstr ""
|
||||
|
||||
msgid "Keyboard and pointer grabbed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Leaving drawing mode"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: %s is a key label
|
||||
#, javascript-format
|
||||
msgid "Press <i>%s</i> for help"
|
||||
msgstr ""
|
||||
|
||||
msgid "Entering drawing mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Global"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter/leave drawing mode"
|
||||
msgid "Internal"
|
||||
msgstr ""
|
||||
|
||||
# There is a similar text in GNOME Boxes (https://gitlab.gnome.org/GNOME/gnome-boxes/tree/master/po)
|
||||
msgid "Grab/ungrab keyboard and pointer"
|
||||
msgid "System"
|
||||
msgstr ""
|
||||
|
||||
msgid "Erase all drawings"
|
||||
msgid "Dashed line"
|
||||
msgstr ""
|
||||
|
||||
msgid "Persistent"
|
||||
#. Translators: as the alternative to "Dashed line"
|
||||
msgid "Full line"
|
||||
msgstr ""
|
||||
|
||||
msgid "Persistent drawing through session restart"
|
||||
msgid "Fill"
|
||||
msgstr ""
|
||||
|
||||
msgid "Drawing on the desktop"
|
||||
#. Translators: as the alternative to "Fill"
|
||||
msgid "Outline"
|
||||
msgstr ""
|
||||
|
||||
msgid "<i>Draw On Your Screen</i> becomes <i>Draw On Your Desktop</i>"
|
||||
#. Translators: fill-rule SVG attribute
|
||||
msgid "Nonzero"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disable on-screen notifications"
|
||||
msgid "Evenodd"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disable panel indicator"
|
||||
#. Translators: generic font-family SVG attribute
|
||||
msgctxt "font-family"
|
||||
msgid "Sans-Serif"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-family"
|
||||
msgid "Serif"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-family"
|
||||
msgid "Monospace"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-family"
|
||||
msgid "Cursive"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-family"
|
||||
msgid "Fantasy"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: font-style SVG attribute
|
||||
msgctxt "font-style"
|
||||
msgid "Normal"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-style"
|
||||
msgid "Oblique"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-style"
|
||||
msgid "Italic"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: font-weight SVG attribute
|
||||
msgctxt "font-weight"
|
||||
msgid "Thin"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Ultra Light"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Light"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Semi Light"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Book"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Normal"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Medium"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Semi Bold"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Ultra Bold"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Heavy"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "font-weight"
|
||||
msgid "Ultra Heavy"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: stroke-linecap SVG attribute
|
||||
msgctxt "stroke-linecap"
|
||||
msgid "Butt"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "stroke-linecap"
|
||||
msgid "Round"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "stroke-linecap"
|
||||
msgid "Square"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: stroke-linejoin SVG attribute
|
||||
msgctxt "stroke-linejoin"
|
||||
msgid "Miter"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "stroke-linejoin"
|
||||
msgid "Round"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "stroke-linejoin"
|
||||
msgid "Bevel"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: value in pixel unit (e.g. "5 px")
|
||||
#, javascript-format
|
||||
msgid "%f px"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: text alignment
|
||||
msgid "Right aligned"
|
||||
msgstr ""
|
||||
|
||||
msgid "Left aligned"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Free drawing"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Line"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Ellipse"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Rectangle"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Text"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Polygon"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Polyline"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Image"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Move"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Resize"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "drawing-tool"
|
||||
msgid "Mirror"
|
||||
msgstr ""
|
||||
|
||||
msgid "Undo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Redo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Erase"
|
||||
msgstr ""
|
||||
|
||||
msgid "Smooth"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open drawing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Save drawing as…"
|
||||
msgstr ""
|
||||
|
||||
msgid "Palette"
|
||||
msgstr ""
|
||||
|
||||
msgid "Color"
|
||||
msgstr ""
|
||||
|
||||
msgid "Type a name"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: "Preferences" page in preferences
|
||||
msgid "Preferences"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: "Drawing" page in preferences
|
||||
msgid "Drawing"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: "About" page in preferences
|
||||
msgid "About"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: you are free to translate the extension name, that is displayed in About page, or not
|
||||
msgid "Draw On You Screen"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: version number in "About" page
|
||||
#, javascript-format
|
||||
msgid "Version %f"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: you are free to translate the extension description, that is displayed in About page, or not
|
||||
msgid ""
|
||||
"Start drawing with Super+Alt+D and save your beautiful work by taking a "
|
||||
"screenshot"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: add your name here or keep it empty, it will be displayed in about page, e.g.
|
||||
#. msgstr ""
|
||||
#. "translator1\n"
|
||||
#. "<a href=\"mailto:translator2@mail.org\">translator2</a>\n"
|
||||
#. "<a href=\"https://...\">translator3</a>"
|
||||
msgid "translator-credits"
|
||||
msgstr ""
|
||||
|
||||
msgid "Palettes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add a new palette"
|
||||
msgstr ""
|
||||
|
||||
msgid "Area"
|
||||
msgstr ""
|
||||
|
||||
msgid "Auto"
|
||||
msgstr ""
|
||||
|
||||
msgid "Grid overlay line"
|
||||
msgstr ""
|
||||
|
||||
msgid "Tools"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dash array"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reset settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rename the palette"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove the palette"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: default name of a new palette
|
||||
msgid "New palette"
|
||||
msgstr ""
|
||||
|
||||
msgid "In drawing mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Draw"
|
||||
|
|
@ -102,17 +380,19 @@ msgstr ""
|
|||
msgid "Scroll"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: %s are key labels (Ctrl+F1 and Ctrl+F9)
|
||||
msgid "Select color"
|
||||
msgstr ""
|
||||
|
||||
# %s are key labels (Ctrl+F1 and Ctrl+F9)
|
||||
#, javascript-format
|
||||
msgid "%s … %s"
|
||||
msgstr ""
|
||||
|
||||
#. Translators: %s is a key label
|
||||
msgid "Ignore pointer movement"
|
||||
msgstr ""
|
||||
|
||||
# %s is a key label
|
||||
#, javascript-format
|
||||
msgid "%s held"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -137,6 +417,9 @@ msgstr ""
|
|||
msgid "Smooth free drawing outline"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unlock image ratio"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rotate <span alpha=\"50%\">(while moving)</span>"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -146,73 +429,224 @@ msgstr ""
|
|||
msgid "Inverse <span alpha=\"50%\">(while mirroring)</span>"
|
||||
msgstr ""
|
||||
|
||||
msgid "Internal"
|
||||
msgid "Drawing on the desktop"
|
||||
msgstr ""
|
||||
|
||||
msgid "(in drawing mode)"
|
||||
msgid "<i>Draw On Your Screen</i> becomes <i>Draw On Your Desktop</i>"
|
||||
msgstr ""
|
||||
|
||||
msgid "Undo last brushstroke"
|
||||
msgid "Erase all drawings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Redo last brushstroke"
|
||||
msgid "Disable panel indicator"
|
||||
msgstr ""
|
||||
|
||||
msgid "Erase last brushstroke"
|
||||
msgid "Disable on-screen notifications"
|
||||
msgstr ""
|
||||
|
||||
msgid "Smooth last brushstroke"
|
||||
msgid "Persistent over toggles"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select line"
|
||||
msgid "Drawing remains when toggling drawing mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select ellipse"
|
||||
msgid "Persistent over restarts"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select rectangle"
|
||||
msgid "Drawing is automatically saved to a file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select polygon"
|
||||
msgid "Enter/leave drawing mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select polyline"
|
||||
#. Translators: there is a similar text in GNOME Boxes (https://gitlab.gnome.org/GNOME/gnome-boxes/tree/master/po)
|
||||
msgid "Grab/ungrab keyboard and pointer"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select image"
|
||||
msgid "Background color"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select text"
|
||||
msgid "The color of the drawing area background"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select move"
|
||||
msgid "Automatic dash array"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select resize"
|
||||
msgid "Compute the lengths from the line width"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select mirror"
|
||||
msgid "Dash array on"
|
||||
msgstr ""
|
||||
|
||||
msgid "Toggle fill/outline"
|
||||
msgid "The dash length in pixels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Increment line width"
|
||||
msgid "Dash array off"
|
||||
msgstr ""
|
||||
|
||||
msgid "The gap between the dashes in pixels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dash offset"
|
||||
msgstr ""
|
||||
|
||||
msgid "The dash offset in pixels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Grid overlay color"
|
||||
msgstr ""
|
||||
|
||||
msgid "The color of the lines"
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatic grid overlay line"
|
||||
msgstr ""
|
||||
|
||||
msgid "Compute the lengths from the screen size"
|
||||
msgstr ""
|
||||
|
||||
msgid "Grid overlay line spacing"
|
||||
msgstr ""
|
||||
|
||||
msgid "The gap between lines in pixels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Grid overlay line width"
|
||||
msgstr ""
|
||||
|
||||
msgid "The line width in pixels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Image location"
|
||||
msgstr ""
|
||||
|
||||
msgid "The location of the directory in which the image tool picks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Color palettes"
|
||||
msgstr ""
|
||||
|
||||
msgid "The palettes of drawing colors"
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatic square area size"
|
||||
msgstr ""
|
||||
|
||||
msgid "Compute the area size from the screen size"
|
||||
msgstr ""
|
||||
|
||||
msgid "Square area size"
|
||||
msgstr ""
|
||||
|
||||
msgid "The size of the area in pixels"
|
||||
msgstr ""
|
||||
|
||||
msgid "Decrement line width"
|
||||
msgstr ""
|
||||
|
||||
msgid "Increment line width even more"
|
||||
msgstr ""
|
||||
|
||||
msgid "Decrement line width even more"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change linejoin"
|
||||
msgid "Erase last brushstroke"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change linecap"
|
||||
msgid "Export drawing to a SVG file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Increment line width"
|
||||
msgstr ""
|
||||
|
||||
msgid "Increment line width even more"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open next drawing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open preferences"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open previous drawing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add images from the clipboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Redo last brushstroke"
|
||||
msgstr ""
|
||||
|
||||
msgid "Save drawing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select color 1"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select color 2"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select color 3"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select color 4"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select color 5"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select color 6"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select color 7"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select color 8"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select color 9"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select ellipse tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select image tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select line tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select mirror tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select move tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select free drawing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select polygon tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select polyline tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select rectangle tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select resize tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select text tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Smooth last brushstroke"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change color palette"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change color palette (reverse)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Toggle fill/outline"
|
||||
msgstr ""
|
||||
|
||||
msgid "Toggle fill rule"
|
||||
|
|
@ -224,289 +658,42 @@ msgstr ""
|
|||
msgid "Change font family (reverse)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change font weight"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change font style"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change font weight"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change image"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change image (reverse)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change linecap"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change linejoin"
|
||||
msgstr ""
|
||||
|
||||
msgid "Toggle text alignment"
|
||||
msgstr ""
|
||||
|
||||
msgid "Change image file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hide panel and dock"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add a drawing background"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add a grid overlay"
|
||||
msgstr ""
|
||||
|
||||
msgid "Square drawing area"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open previous drawing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open next drawing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Save drawing as a SVG file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit style"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open preferences"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show help"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"<b>Default</b> drawing style attributes (color palette, font, line, dash) are defined in an editable <b>css</b> file.\n"
|
||||
"See <i>“%s”</i>."
|
||||
msgid "Hide panel and dock"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"When you save elements made with <b>eraser</b> in a <b>SVG</b> file, "
|
||||
"they are colored with background color, transparent if it is disabled.\n"
|
||||
"See <i>“%s”</i> or edit the SVG file afterwards."
|
||||
#. Translators: It is an action: "Make the drawing area a square"
|
||||
msgid "Square drawing area"
|
||||
msgstr ""
|
||||
|
||||
msgid "Screenshot"
|
||||
msgid "Undo last brushstroke"
|
||||
msgstr ""
|
||||
|
||||
msgid "Screenshot to clipboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Area screenshot"
|
||||
msgstr ""
|
||||
|
||||
msgid "Area screenshot to clipboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "System"
|
||||
msgstr ""
|
||||
|
||||
msgid "Undo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Redo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Erase"
|
||||
msgstr ""
|
||||
|
||||
msgid "Smooth"
|
||||
msgstr ""
|
||||
|
||||
msgid "Free drawing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Line"
|
||||
msgstr ""
|
||||
|
||||
msgid "Ellipse"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rectangle"
|
||||
msgstr ""
|
||||
|
||||
msgid "Text"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polyline"
|
||||
msgstr ""
|
||||
|
||||
msgid "Image"
|
||||
msgstr ""
|
||||
|
||||
msgid "Move"
|
||||
msgstr ""
|
||||
|
||||
msgid "Resize"
|
||||
msgstr ""
|
||||
|
||||
msgid "Mirror"
|
||||
msgstr ""
|
||||
|
||||
msgid "Color"
|
||||
msgstr ""
|
||||
|
||||
msgid "Fill"
|
||||
msgstr ""
|
||||
|
||||
# fill-rule SVG attribute
|
||||
msgid "Evenodd"
|
||||
msgstr ""
|
||||
|
||||
msgid "%d px"
|
||||
msgstr ""
|
||||
|
||||
# stroke-linejoin SVG attribute
|
||||
msgid "Miter"
|
||||
msgstr ""
|
||||
|
||||
# stroke-linejoin and stroke-linecap SVG attribute
|
||||
msgid "Round"
|
||||
msgstr ""
|
||||
|
||||
# stroke-linejoin SVG attribute
|
||||
msgid "Bevel"
|
||||
msgstr ""
|
||||
|
||||
# stroke-linecap SVG attribute
|
||||
msgid "Butt"
|
||||
msgstr ""
|
||||
|
||||
# stroke-linecap SVG attribute
|
||||
msgid "Square"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dashed"
|
||||
msgstr ""
|
||||
|
||||
# generic font-family SVG attribute
|
||||
msgid "Sans-Serif"
|
||||
msgstr ""
|
||||
|
||||
# generic font-family SVG attribute
|
||||
msgid "Serif"
|
||||
msgstr ""
|
||||
|
||||
# generic font-family SVG attribute
|
||||
msgid "Monospace"
|
||||
msgstr ""
|
||||
|
||||
# generic font-family SVG attribute
|
||||
msgid "Cursive"
|
||||
msgstr ""
|
||||
|
||||
# generic font-family SVG attribute
|
||||
msgid "Fantasy"
|
||||
msgstr ""
|
||||
|
||||
# font-weight SVG attribute
|
||||
msgid "Thin"
|
||||
msgstr ""
|
||||
|
||||
# font-weight SVG attribute
|
||||
msgid "Ultra-light"
|
||||
msgstr ""
|
||||
|
||||
# font-weight SVG attribute
|
||||
msgid "Light"
|
||||
msgstr ""
|
||||
|
||||
# font-weight SVG attribute
|
||||
msgid "Semi-light"
|
||||
msgstr ""
|
||||
|
||||
# font-weight SVG attribute
|
||||
msgid "Book"
|
||||
msgstr ""
|
||||
|
||||
# font-weight and font-style SVG attribute
|
||||
msgid "Normal"
|
||||
msgstr ""
|
||||
|
||||
# font-weight SVG attribute
|
||||
msgid "Medium"
|
||||
msgstr ""
|
||||
|
||||
# font-weight SVG attribute
|
||||
msgid "Semi-bold"
|
||||
msgstr ""
|
||||
|
||||
# font-weight SVG attribute
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
# font-weight SVG attribute
|
||||
msgid "Ultra-bold"
|
||||
msgstr ""
|
||||
|
||||
# font-weight SVG attribute
|
||||
msgid "Heavy"
|
||||
msgstr ""
|
||||
|
||||
# font-style SVG attribute
|
||||
msgid "Italic"
|
||||
msgstr ""
|
||||
|
||||
# font-style SVG attribute
|
||||
msgid "Oblique"
|
||||
msgstr ""
|
||||
|
||||
msgid "Right aligned"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open drawing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Save drawing"
|
||||
msgstr ""
|
||||
|
||||
msgid "Leaving drawing mode"
|
||||
msgstr ""
|
||||
|
||||
# %s is a key label
|
||||
msgid "<small>Press <i>%s</i> for help</small>"
|
||||
msgstr ""
|
||||
|
||||
msgid "Entering drawing mode"
|
||||
msgstr ""
|
||||
|
||||
# "released" as the opposite of "grabbed"
|
||||
msgid "Keyboard and pointer released"
|
||||
msgstr ""
|
||||
|
||||
msgid "Keyboard and pointer grabbed"
|
||||
msgstr ""
|
||||
|
||||
# %s is a key label
|
||||
msgid ""
|
||||
"Press <i>%s</i> to get\n"
|
||||
"a fourth control point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Mark a point of symmetry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Draw a line of symmetry"
|
||||
msgstr ""
|
||||
|
||||
# %s is a key label
|
||||
msgid ""
|
||||
"Press <i>%s</i> to mark vertices"
|
||||
msgstr ""
|
||||
|
||||
# %s is a key label
|
||||
msgid ""
|
||||
"Type your text and press <i>%s</i>"
|
||||
msgstr ""
|
||||
|
||||
# as the alternative to "Fill"
|
||||
msgid "Outline"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dashed line"
|
||||
msgstr ""
|
||||
|
||||
msgid "Full line"
|
||||
msgstr ""
|
||||
|
||||
msgid "Left aligned"
|
||||
msgstr ""
|
||||
|
||||
msgid "Nonzero"
|
||||
msgstr ""
|
||||
|
||||
|
||||
|
|
|
|||
577
menu.js
|
|
@ -1,4 +1,5 @@
|
|||
/* jslint esversion: 6 */
|
||||
/* exported DisplayStrings, DrawingMenu */
|
||||
|
||||
/*
|
||||
* Copyright 2019 Abakkk
|
||||
|
|
@ -21,7 +22,6 @@
|
|||
*/
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
|
|
@ -30,44 +30,121 @@ const St = imports.gi.St;
|
|||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Config = imports.misc.config;
|
||||
const Dash = imports.ui.dash;
|
||||
const Main = imports.ui.main;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Slider = imports.ui.slider;
|
||||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Area = Me.imports.area;
|
||||
const Elements = Me.imports.elements;
|
||||
const Extension = Me.imports.extension;
|
||||
const Files = Me.imports.files;
|
||||
const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext;
|
||||
const pgettext = imports.gettext.domain(Me.metadata['gettext-domain']).pgettext;
|
||||
|
||||
const GS_VERSION = Config.PACKAGE_VERSION;
|
||||
|
||||
const ICON_DIR = Me.dir.get_child('data').get_child('icons');
|
||||
const SMOOTH_ICON_PATH = ICON_DIR.get_child('smooth-symbolic.svg').get_path();
|
||||
const COLOR_ICON_PATH = ICON_DIR.get_child('color-symbolic.svg').get_path();
|
||||
const FILL_ICON_PATH = ICON_DIR.get_child('fill-symbolic.svg').get_path();
|
||||
const STROKE_ICON_PATH = ICON_DIR.get_child('stroke-symbolic.svg').get_path();
|
||||
const LINEJOIN_ICON_PATH = ICON_DIR.get_child('linejoin-symbolic.svg').get_path();
|
||||
const LINECAP_ICON_PATH = ICON_DIR.get_child('linecap-symbolic.svg').get_path();
|
||||
const FILLRULE_NONZERO_ICON_PATH = ICON_DIR.get_child('fillrule-nonzero-symbolic.svg').get_path();
|
||||
const FILLRULE_EVENODD_ICON_PATH = ICON_DIR.get_child('fillrule-evenodd-symbolic.svg').get_path();
|
||||
const DASHED_LINE_ICON_PATH = ICON_DIR.get_child('dashed-line-symbolic.svg').get_path();
|
||||
const FULL_LINE_ICON_PATH = ICON_DIR.get_child('full-line-symbolic.svg').get_path();
|
||||
|
||||
// 150 labels with font-family style take ~15Mo
|
||||
const FONT_FAMILY_STYLE = true;
|
||||
// use 'login-dialog-message-warning' class in order to get GS theme warning color (default: #f57900)
|
||||
const WARNING_COLOR_STYLE_CLASS_NAME = 'login-dialog-message-warning';
|
||||
|
||||
const getActor = function(object) {
|
||||
return GS_VERSION < '3.33.0' ? object.actor : object;
|
||||
};
|
||||
|
||||
const getSummary = function(settingKey) {
|
||||
return Me.internalShortcutSettings.settings_schema.get_key(settingKey).get_summary();
|
||||
};
|
||||
|
||||
// Used by both menu and osd notifications.
|
||||
var DisplayStrings = {
|
||||
getDashedLine: function(dashed) {
|
||||
return dashed ? _("Dashed line") :
|
||||
// Translators: as the alternative to "Dashed line"
|
||||
_("Full line");
|
||||
},
|
||||
|
||||
getFill: function(fill) {
|
||||
return fill ? _("Fill") :
|
||||
// Translators: as the alternative to "Fill"
|
||||
_("Outline");
|
||||
},
|
||||
|
||||
get FillRule() {
|
||||
if (!this._fillRules)
|
||||
// Translators: fill-rule SVG attribute
|
||||
this._fillRules = { 0: _("Nonzero"), 1: _("Evenodd") };
|
||||
return this._fillRules;
|
||||
},
|
||||
|
||||
getFontFamily: function(family) {
|
||||
if (!this._fontGenericFamilies)
|
||||
// Translators: generic font-family SVG attribute
|
||||
this._fontGenericFamilies = { 'Sans-Serif': pgettext("font-family", "Sans-Serif"), 'Serif': pgettext("font-family", "Serif"),
|
||||
'Monospace': pgettext("font-family", "Monospace"), 'Cursive': pgettext("font-family", "Cursive"),
|
||||
'Fantasy': pgettext("font-family", "Fantasy") };
|
||||
return this._fontGenericFamilies[family] || family;
|
||||
},
|
||||
|
||||
get FontStyle() {
|
||||
if (!this._fontStyles)
|
||||
// Translators: font-style SVG attribute
|
||||
this._fontStyles = { 0: pgettext("font-style", "Normal"), 1: pgettext("font-style", "Oblique"), 2: pgettext("font-style", "Italic") };
|
||||
return this._fontStyles;
|
||||
},
|
||||
|
||||
FontStyleMarkup: { 0: 'normal', 1: 'oblique', 2: 'italic' },
|
||||
|
||||
get FontWeight() {
|
||||
if (!this._fontWeights)
|
||||
// Translators: font-weight SVG attribute
|
||||
this._fontWeights = { 100: pgettext("font-weight", "Thin"), 200: pgettext("font-weight", "Ultra Light"), 300: pgettext("font-weight", "Light"),
|
||||
350: pgettext("font-weight", "Semi Light"), 380: pgettext("font-weight", "Book"), 400: pgettext("font-weight", "Normal"),
|
||||
500: pgettext("font-weight", "Medium"), 600: pgettext("font-weight", "Semi Bold"), 700: pgettext("font-weight", "Bold"),
|
||||
800: pgettext("font-weight", "Ultra Bold"), 900: pgettext("font-weight", "Heavy"), 1000: pgettext("font-weight", "Ultra Heavy") };
|
||||
return this._fontWeights;
|
||||
},
|
||||
|
||||
get LineCap() {
|
||||
if (!this._lineCaps)
|
||||
// Translators: stroke-linecap SVG attribute
|
||||
this._lineCaps = { 0: pgettext("stroke-linecap", "Butt"), 1: pgettext("stroke-linecap", "Round"), 2: pgettext("stroke-linecap", "Square") };
|
||||
return this._lineCaps;
|
||||
},
|
||||
|
||||
get LineJoin() {
|
||||
if (!this._lineJoins)
|
||||
// Translators: stroke-linejoin SVG attribute
|
||||
this._lineJoins = { 0: pgettext("stroke-linejoin", "Miter"), 1: pgettext("stroke-linejoin", "Round"), 2: pgettext("stroke-linejoin", "Bevel") };
|
||||
return this._lineJoins;
|
||||
},
|
||||
|
||||
getPixels(value) {
|
||||
// Translators: value in pixel unit (e.g. "5 px")
|
||||
return _("%f px").format(value);
|
||||
},
|
||||
|
||||
getTextAlignment: function(rightAligned) {
|
||||
// Translators: text alignment
|
||||
return rightAligned ? _("Right aligned") : _("Left aligned");
|
||||
},
|
||||
|
||||
get Tool() {
|
||||
if (!this._tools)
|
||||
this._tools = { 0: pgettext("drawing-tool", "Free drawing"), 1: pgettext("drawing-tool", "Line"), 2: pgettext("drawing-tool", "Ellipse"),
|
||||
3: pgettext("drawing-tool", "Rectangle"), 4: pgettext("drawing-tool", "Text"), 5: pgettext("drawing-tool", "Polygon"),
|
||||
6: pgettext("drawing-tool", "Polyline"), 7: pgettext("drawing-tool", "Image"),
|
||||
100: pgettext("drawing-tool", "Move"), 101: pgettext("drawing-tool", "Resize"), 102: pgettext("drawing-tool", "Mirror") };
|
||||
return this._tools;
|
||||
}
|
||||
};
|
||||
|
||||
var DrawingMenu = new Lang.Class({
|
||||
Name: 'DrawOnYourScreenDrawingMenu',
|
||||
|
||||
_init: function(area, monitor) {
|
||||
_init: function(area, monitor, drawingTools) {
|
||||
this.area = area;
|
||||
this.drawingTools = drawingTools;
|
||||
|
||||
let side = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL ? St.Side.RIGHT : St.Side.LEFT;
|
||||
this.menu = new PopupMenu.PopupMenu(Main.layoutManager.dummyCursor, 0.25, side);
|
||||
this.menuManager = new PopupMenu.PopupMenuManager(GS_VERSION < '3.33.0' ? { actor: this.area } : this.area);
|
||||
|
|
@ -94,20 +171,11 @@ var DrawingMenu = new Lang.Class({
|
|||
this.saveDrawingSubMenu.close();
|
||||
menuCloseFunc.bind(this.menu)(animate);
|
||||
};
|
||||
|
||||
this.colorIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(COLOR_ICON_PATH) });
|
||||
this.smoothIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(SMOOTH_ICON_PATH) });
|
||||
this.strokeIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(STROKE_ICON_PATH) });
|
||||
this.fillIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FILL_ICON_PATH) });
|
||||
this.fillRuleNonzeroIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FILLRULE_NONZERO_ICON_PATH) });
|
||||
this.fillRuleEvenoddIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FILLRULE_EVENODD_ICON_PATH) });
|
||||
this.linejoinIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(LINEJOIN_ICON_PATH) });
|
||||
this.linecapIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(LINECAP_ICON_PATH) });
|
||||
this.fullLineIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FULL_LINE_ICON_PATH) });
|
||||
this.dashedLineIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(DASHED_LINE_ICON_PATH) });
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
delete this.area;
|
||||
delete this.drawingTools;
|
||||
this.menuManager.removeMenu(this.menu);
|
||||
Main.layoutManager.uiGroup.remove_actor(this.menu.actor);
|
||||
this.menu.destroy();
|
||||
|
|
@ -154,106 +222,101 @@ var DrawingMenu = new Lang.Class({
|
|||
_redisplay: function() {
|
||||
this.menu.removeAll();
|
||||
|
||||
this.actionButtons = [];
|
||||
let groupItem = new PopupMenu.PopupBaseMenuItem({ reactive: false, can_focus: false, style_class: "draw-on-your-screen-menu-group-item" });
|
||||
getActor(groupItem).add_child(this._createActionButton(_("Undo"), this.area.undo.bind(this.area), 'edit-undo-symbolic'));
|
||||
getActor(groupItem).add_child(this._createActionButton(_("Redo"), this.area.redo.bind(this.area), 'edit-redo-symbolic'));
|
||||
getActor(groupItem).add_child(this._createActionButton(_("Erase"), this.area.deleteLastElement.bind(this.area), 'edit-clear-all-symbolic'));
|
||||
getActor(groupItem).add_child(this._createActionButton(_("Smooth"), this.area.smoothLastElement.bind(this.area), this.smoothIcon));
|
||||
let groupItem = new PopupMenu.PopupBaseMenuItem({ reactive: false, can_focus: false, style_class: 'draw-on-your-screen-menu-group-item' });
|
||||
this.undoButton = new ActionButton(_("Undo"), 'edit-undo-symbolic', this.area.undo.bind(this.area), this._updateActionSensitivity.bind(this));
|
||||
this.redoButton = new ActionButton(_("Redo"), 'edit-redo-symbolic', this.area.redo.bind(this.area), this._updateActionSensitivity.bind(this));
|
||||
this.eraseButton = new ActionButton(_("Erase"), 'edit-clear-all-symbolic', this.area.deleteLastElement.bind(this.area), this._updateActionSensitivity.bind(this));
|
||||
this.smoothButton = new ActionButton(_("Smooth"), Files.Icons.SMOOTH, this.area.smoothLastElement.bind(this.area), this._updateActionSensitivity.bind(this));
|
||||
this.eraseButton.child.add_style_class_name('draw-on-your-screen-menu-destructive-button');
|
||||
this.smoothButton.child.add_style_class_name('draw-on-your-screen-menu-destructive-button');
|
||||
getActor(groupItem).add_child(this.undoButton);
|
||||
getActor(groupItem).add_child(this.redoButton);
|
||||
getActor(groupItem).add_child(this.eraseButton);
|
||||
getActor(groupItem).add_child(this.smoothButton);
|
||||
this.menu.addMenuItem(groupItem);
|
||||
this._addSeparator(this.menu, true);
|
||||
|
||||
this._addSubMenuItem(this.menu, 'document-edit-symbolic', Area.ToolNames, this.area, 'currentTool', this._updateSectionVisibility.bind(this));
|
||||
this.colorItem = this._addColorSubMenuItem(this.menu);
|
||||
this.fillItem = this._addSwitchItem(this.menu, _("Fill"), this.strokeIcon, this.fillIcon, this.area, 'fill', this._updateSectionVisibility.bind(this));
|
||||
this.toolItem = this._addToolSubMenuItem(this.menu, this._updateSectionVisibility.bind(this));
|
||||
this.paletteItem = this._addPaletteSubMenuItem(this.menu, Files.Icons.PALETTE);
|
||||
this.colorItem = this._addColorSubMenuItem(this.menu, Files.Icons.COLOR);
|
||||
this.fillItem = this._addSwitchItem(this.menu, DisplayStrings.getFill(true), Files.Icons.STROKE, Files.Icons.FILL, this.area, 'fill', this._updateSectionVisibility.bind(this));
|
||||
this.fillSection = new PopupMenu.PopupMenuSection();
|
||||
this.fillSection.itemActivated = () => {};
|
||||
this.fillRuleItem = this._addSwitchItem(this.fillSection, _("Evenodd"), this.fillRuleNonzeroIcon, this.fillRuleEvenoddIcon, this.area, 'currentEvenodd');
|
||||
this.fillRuleItem = this._addSwitchItem(this.fillSection, DisplayStrings.FillRule[1], Files.Icons.FILLRULE_NONZERO, Files.Icons.FILLRULE_EVENODD, this.area, 'currentEvenodd');
|
||||
this.menu.addMenuItem(this.fillSection);
|
||||
this._addSeparator(this.menu);
|
||||
|
||||
let lineSection = new PopupMenu.PopupMenuSection();
|
||||
this._addSliderItem(lineSection, this.area, 'currentLineWidth');
|
||||
this._addSubMenuItem(lineSection, this.linejoinIcon, Elements.LineJoinNames, this.area, 'currentLineJoin');
|
||||
this._addSubMenuItem(lineSection, this.linecapIcon, Elements.LineCapNames, this.area, 'currentLineCap');
|
||||
this._addSwitchItem(lineSection, _("Dashed"), this.fullLineIcon, this.dashedLineIcon, this.area, 'dashedLine');
|
||||
this._addSubMenuItem(lineSection, Files.Icons.LINEJOIN, DisplayStrings.LineJoin, this.area, 'currentLineJoin');
|
||||
this._addSubMenuItem(lineSection, Files.Icons.LINECAP, DisplayStrings.LineCap, this.area, 'currentLineCap');
|
||||
this._addSwitchItem(lineSection, DisplayStrings.getDashedLine(true), Files.Icons.FULL_LINE, Files.Icons.DASHED_LINE, this.area, 'dashedLine');
|
||||
this._addSeparator(lineSection);
|
||||
this.menu.addMenuItem(lineSection);
|
||||
lineSection.itemActivated = () => {};
|
||||
this.lineSection = lineSection;
|
||||
|
||||
let fontSection = new PopupMenu.PopupMenuSection();
|
||||
this._addFontFamilySubMenuItem(fontSection, 'font-x-generic-symbolic');
|
||||
this._addSubMenuItem(fontSection, 'format-text-bold-symbolic', Elements.FontWeightNames, this.area, 'currentFontWeight');
|
||||
this._addSubMenuItem(fontSection, 'format-text-italic-symbolic', Elements.FontStyleNames, this.area, 'currentFontStyle');
|
||||
this._addSwitchItem(fontSection, _("Right aligned"), 'format-justify-left-symbolic', 'format-justify-right-symbolic', this.area, 'currentTextRightAligned');
|
||||
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._addSeparator(fontSection);
|
||||
this.menu.addMenuItem(fontSection);
|
||||
fontSection.itemActivated = () => {};
|
||||
this.fontSection = fontSection;
|
||||
|
||||
let imageSection = new PopupMenu.PopupMenuSection();
|
||||
let images = this.area.getImages();
|
||||
if (images.length) {
|
||||
if (this.area.currentImage > images.length - 1)
|
||||
this.area.currentImage = images.length - 1;
|
||||
this._addSubMenuItem(imageSection, null, images, this.area, 'currentImage');
|
||||
}
|
||||
this.imageItem = this._addImageSubMenuItem(imageSection);
|
||||
this._addSeparator(imageSection);
|
||||
this.menu.addMenuItem(imageSection);
|
||||
imageSection.itemActivated = () => {};
|
||||
this.imageSection = imageSection;
|
||||
|
||||
let manager = Extension.manager;
|
||||
this._addSimpleSwitchItem(this.menu, _("Hide panel and dock"), manager.hiddenList ? true : false, manager.togglePanelAndDockOpacity.bind(manager));
|
||||
this._addSimpleSwitchItem(this.menu, _("Add a drawing background"), this.area.hasBackground, this.area.toggleBackground.bind(this.area));
|
||||
this._addSimpleSwitchItem(this.menu, _("Add a grid overlay"), this.area.hasGrid, this.area.toggleGrid.bind(this.area));
|
||||
this._addSimpleSwitchItem(this.menu, _("Square drawing area"), this.area.isSquareArea, this.area.toggleSquareArea.bind(this.area));
|
||||
let areaManager = Me.stateObj.areaManager;
|
||||
this._addSimpleSwitchItem(this.menu, getSummary('toggle-panel-and-dock-visibility'), !!areaManager.hiddenList, areaManager.togglePanelAndDockOpacity.bind(areaManager));
|
||||
this._addSimpleSwitchItem(this.menu, getSummary('toggle-background'), this.area.hasBackground, this.area.toggleBackground.bind(this.area));
|
||||
this._addSimpleSwitchItem(this.menu, getSummary('toggle-grid'), this.area.hasGrid, this.area.toggleGrid.bind(this.area));
|
||||
this._addSimpleSwitchItem(this.menu, getSummary('toggle-square-area'), this.area.isSquareArea, this.area.toggleSquareArea.bind(this.area));
|
||||
this._addSeparator(this.menu);
|
||||
|
||||
this._addDrawingNameItem(this.menu);
|
||||
this._addOpenDrawingSubMenuItem(this.menu);
|
||||
this._addSaveDrawingSubMenuItem(this.menu);
|
||||
this._addOpenDrawingSubMenuItem(this.menu, _("Open drawing"), 'document-open-symbolic');
|
||||
this._addSaveDrawingSubMenuItem(this.menu, _("Save drawing as…"), 'document-save-as-symbolic');
|
||||
this._addSeparator(this.menu);
|
||||
|
||||
this.menu.addAction(_("Save drawing as a SVG file"), this.area.saveAsSvg.bind(this.area), 'image-x-generic-symbolic');
|
||||
this.menu.addAction(_("Edit style"), manager.openUserStyleFile.bind(manager), 'document-page-setup-symbolic');
|
||||
this.menu.addAction(_("Show help"), () => { this.close(); this.area.toggleHelp(); }, 'preferences-desktop-keyboard-shortcuts-symbolic');
|
||||
groupItem = new PopupMenu.PopupBaseMenuItem({ reactive: false, can_focus: false, style_class: 'draw-on-your-screen-menu-group-item' });
|
||||
this.saveButton = new ActionButton(getSummary('save-as-json'), 'document-save-symbolic', this.area.saveAsJson.bind(this.area, false, this._onDrawingSaved.bind(this)), null);
|
||||
this.svgButton = new ActionButton(getSummary('export-to-svg'), Files.Icons.DOCUMENT_EXPORT, this.area.exportToSvg.bind(this.area), null);
|
||||
this.prefsButton = new ActionButton(getSummary('open-preferences'), 'document-page-setup-symbolic', areaManager.openPreferences.bind(areaManager), null);
|
||||
this.helpButton = new ActionButton(getSummary('toggle-help'), 'preferences-desktop-keyboard-shortcuts-symbolic', () => { this.close(); this.area.toggleHelp(); }, null);
|
||||
getActor(groupItem).add_child(this.saveButton);
|
||||
getActor(groupItem).add_child(this.svgButton);
|
||||
getActor(groupItem).add_child(this.prefsButton);
|
||||
getActor(groupItem).add_child(this.helpButton);
|
||||
this.menu.addMenuItem(groupItem);
|
||||
|
||||
this._updateActionSensitivity();
|
||||
this._updateSectionVisibility();
|
||||
},
|
||||
|
||||
// from system.js (GS 3.34-)
|
||||
_createActionButton: function(accessibleName, callback, icon) {
|
||||
let button = new St.Button({ track_hover: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
accessible_name: accessibleName,
|
||||
// use 'popup-menu' and 'popup-menu-item' style classes to provide theme colors
|
||||
style_class: 'system-menu-action popup-menu-item popup-menu' });
|
||||
button.child = new St.Icon(typeof icon == 'string' ? { icon_name: icon } : { gicon: icon });
|
||||
button.connect('clicked', () => {
|
||||
callback();
|
||||
this._updateActionSensitivity();
|
||||
});
|
||||
button.bind_property('reactive', button, 'can_focus', GObject.BindingFlags.DEFAULT);
|
||||
this.actionButtons.push(button);
|
||||
return new St.Bin({ child: button, x_expand: true });
|
||||
},
|
||||
|
||||
_updateActionSensitivity: function() {
|
||||
let [undoButton, redoButton, eraseButton, smoothButton] = this.actionButtons;
|
||||
undoButton.reactive = this.area.elements.length > 0;
|
||||
redoButton.reactive = this.area.undoneElements.length > 0;
|
||||
eraseButton.reactive = this.area.elements.length > 0;
|
||||
smoothButton.reactive = this.area.elements.length > 0 && this.area.elements[this.area.elements.length - 1].shape == Area.Tools.NONE;
|
||||
this.undoButton.child.reactive = this.area.elements.length > 0;
|
||||
this.redoButton.child.reactive = this.area.undoneElements.length > 0;
|
||||
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.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 == Area.Tools.TEXT, this.area.currentTool == Area.Tools.IMAGE];
|
||||
let [isText, isImage] = [this.area.currentTool == this.drawingTools.TEXT, this.area.currentTool == this.drawingTools.IMAGE];
|
||||
this.lineSection.actor.visible = !isText && !isImage;
|
||||
this.fontSection.actor.visible = isText;
|
||||
this.imageSection.actor.visible = isImage;
|
||||
this.colorItem.setSensitive(!isImage);
|
||||
this.paletteItem.setSensitive(!isImage);
|
||||
this.fillItem.setSensitive(!isText && !isImage);
|
||||
this.fillSection.setSensitive(!isText && !isImage);
|
||||
|
||||
|
|
@ -269,18 +332,14 @@ var DrawingMenu = new Lang.Class({
|
|||
item.icon = new St.Icon({ style_class: 'popup-menu-icon' });
|
||||
getActor(item).insert_child_at_index(item.icon, 1);
|
||||
let icon = target[targetProperty] ? iconTrue : iconFalse;
|
||||
if (icon && icon instanceof GObject.Object && GObject.type_is_a(icon, Gio.Icon))
|
||||
if (icon)
|
||||
item.icon.set_gicon(icon);
|
||||
else if (icon)
|
||||
item.icon.set_icon_name(icon);
|
||||
|
||||
item.connect('toggled', (item, state) => {
|
||||
target[targetProperty] = state;
|
||||
let icon = target[targetProperty] ? iconTrue : iconFalse;
|
||||
if (icon && icon instanceof GObject.Object && GObject.type_is_a(icon, Gio.Icon))
|
||||
if (icon)
|
||||
item.icon.set_gicon(icon);
|
||||
else if (icon)
|
||||
item.icon.set_icon_name(icon);
|
||||
if (onToggled)
|
||||
onToggled();
|
||||
});
|
||||
|
|
@ -296,26 +355,26 @@ var DrawingMenu = new Lang.Class({
|
|||
|
||||
_addSliderItem: function(menu, target, targetProperty) {
|
||||
let item = new PopupMenu.PopupBaseMenuItem({ activate: false });
|
||||
let label = new St.Label({ text: _("%d px").format(target[targetProperty]), style_class: 'draw-on-your-screen-menu-slider-label' });
|
||||
let label = new St.Label({ text: DisplayStrings.getPixels(target[targetProperty]), style_class: 'draw-on-your-screen-menu-slider-label' });
|
||||
let slider = new Slider.Slider(target[targetProperty] / 50);
|
||||
|
||||
if (GS_VERSION < '3.33.0') {
|
||||
slider.connect('value-changed', (slider, value, property) => {
|
||||
target[targetProperty] = Math.max(Math.round(value * 50), 0);
|
||||
label.set_text(target[targetProperty] + " px");
|
||||
label.set_text(DisplayStrings.getPixels(target[targetProperty]));
|
||||
if (target[targetProperty] === 0)
|
||||
label.add_style_class_name(Extension.WARNING_COLOR_STYLE_CLASS_NAME);
|
||||
label.add_style_class_name(WARNING_COLOR_STYLE_CLASS_NAME);
|
||||
else
|
||||
label.remove_style_class_name(Extension.WARNING_COLOR_STYLE_CLASS_NAME);
|
||||
label.remove_style_class_name(WARNING_COLOR_STYLE_CLASS_NAME);
|
||||
});
|
||||
} else {
|
||||
slider.connect('notify::value', () => {
|
||||
target[targetProperty] = Math.max(Math.round(slider.value * 50), 0);
|
||||
label.set_text(target[targetProperty] + " px");
|
||||
label.set_text(DisplayStrings.getPixels(target[targetProperty]));
|
||||
if (target[targetProperty] === 0)
|
||||
label.add_style_class_name(Extension.WARNING_COLOR_STYLE_CLASS_NAME);
|
||||
label.add_style_class_name(WARNING_COLOR_STYLE_CLASS_NAME);
|
||||
else
|
||||
label.remove_style_class_name(Extension.WARNING_COLOR_STYLE_CLASS_NAME);
|
||||
label.remove_style_class_name(WARNING_COLOR_STYLE_CLASS_NAME);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -327,94 +386,139 @@ var DrawingMenu = new Lang.Class({
|
|||
menu.addMenuItem(item);
|
||||
},
|
||||
|
||||
_addSubMenuItem: function(menu, icon, obj, target, targetProperty, callback) {
|
||||
if (targetProperty == 'currentImage')
|
||||
icon = obj[target[targetProperty]].gicon;
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(_(String(obj[target[targetProperty]])), icon ? true : false);
|
||||
if (icon && icon instanceof GObject.Object && GObject.type_is_a(icon, Gio.Icon))
|
||||
item.icon.set_gicon(icon);
|
||||
else if (icon)
|
||||
item.icon.set_icon_name(icon);
|
||||
_addSubMenuItem: function(menu, icon, obj, target, targetProperty) {
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(String(obj[target[targetProperty]]), icon ? true : false);
|
||||
|
||||
item.menu.itemActivated = () => {
|
||||
item.menu.close();
|
||||
};
|
||||
item.icon.set_gicon(icon);
|
||||
item.menu.itemActivated = item.menu.close;
|
||||
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||
for (let i in obj) {
|
||||
let text;
|
||||
if (targetProperty == 'currentFontWeight')
|
||||
text = `<span font_weight="${i}">${_(obj[i])}</span>`;
|
||||
else if (targetProperty == 'currentFontStyle')
|
||||
text = `<span font_style="${obj[i].toLowerCase()}">${_(obj[i])}</span>`;
|
||||
else
|
||||
text = _(String(obj[i]));
|
||||
Object.keys(obj).forEach(key => {
|
||||
let text = targetProperty == 'currentFontWeight' ? `<span font_weight="${key}">${obj[key]}</span>` :
|
||||
targetProperty == 'currentFontStyle' ? `<span font_style="${DisplayStrings.FontStyleMarkup[key]}">${obj[key]}</span>` :
|
||||
String(obj[key]);
|
||||
|
||||
let iCaptured = Number(i);
|
||||
let subItem = item.menu.addAction(text, () => {
|
||||
item.label.set_text(_(String(obj[iCaptured])));
|
||||
target[targetProperty] = iCaptured;
|
||||
if (targetProperty == 'currentImage')
|
||||
item.icon.set_gicon(obj[iCaptured].gicon);
|
||||
if (callback)
|
||||
callback();
|
||||
item.label.set_text(String(obj[key]));
|
||||
target[targetProperty] = Number(key);
|
||||
});
|
||||
|
||||
subItem.label.get_clutter_text().set_use_markup(true);
|
||||
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
||||
});
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
|
||||
menu.addMenuItem(item);
|
||||
},
|
||||
|
||||
_addToolSubMenuItem: function(menu, callback) {
|
||||
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);
|
||||
item.icon.set_gicon(Files.Icons[`TOOL_${toolName}`]);
|
||||
};
|
||||
item.update();
|
||||
|
||||
item.menu.itemActivated = item.menu.close;
|
||||
|
||||
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 subItemIcon = Files.Icons[`TOOL_${toolName}`];
|
||||
let subItem = item.menu.addAction(text, () => {
|
||||
this.area.currentTool = Number(key);
|
||||
item.update();
|
||||
callback();
|
||||
}, subItemIcon);
|
||||
|
||||
subItem.label.get_clutter_text().set_use_markup(true);
|
||||
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
||||
|
||||
// change the display order of tools
|
||||
if (obj == Area.ToolNames && i == Area.Tools.POLYGON)
|
||||
item.menu.moveMenuItem(subItem, 4);
|
||||
else if (obj == Area.ToolNames && i == Area.Tools.POLYLINE)
|
||||
item.menu.moveMenuItem(subItem, 5);
|
||||
}
|
||||
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);
|
||||
});
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
menu.addMenuItem(item);
|
||||
},
|
||||
|
||||
_addColorSubMenuItem: function(menu) {
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(_("Color"), true);
|
||||
item.icon.set_gicon(this.colorIcon);
|
||||
item.icon.set_style(`color:${this.area.currentColor.to_string().slice(0, 7)};`);
|
||||
|
||||
item.menu.itemActivated = () => {
|
||||
item.menu.close();
|
||||
};
|
||||
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||
for (let i = 1; i < this.area.colors.length; i++) {
|
||||
let text = this.area.colors[i].to_string();
|
||||
let iCaptured = i;
|
||||
let colorItem = item.menu.addAction(text, () => {
|
||||
this.area.currentColor = this.area.colors[iCaptured];
|
||||
item.icon.set_style(`color:${this.area.currentColor.to_string().slice(0, 7)};`);
|
||||
});
|
||||
// Foreground color markup is not displayed since 3.36, use style instead but the transparency is lost.
|
||||
colorItem.label.set_style(`color:${this.area.colors[i].to_string().slice(0, 7)};`);
|
||||
getActor(colorItem).connect('key-focus-in', updateSubMenuAdjustment);
|
||||
}
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
menu.addMenuItem(item);
|
||||
return item;
|
||||
},
|
||||
|
||||
_addFontFamilySubMenuItem: function(menu, icon) {
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(this.area.currentFontFamily, true);
|
||||
item.icon.set_icon_name(icon);
|
||||
_addPaletteSubMenuItem: function(menu, icon) {
|
||||
let text = _(this.area.currentPalette[0] || "Palette");
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(text, true);
|
||||
item.icon.set_gicon(icon);
|
||||
|
||||
item.menu.itemActivated = () => {
|
||||
item.menu.close();
|
||||
};
|
||||
item.menu.itemActivated = item.menu.close;
|
||||
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||
this.area.palettes.forEach(palette => {
|
||||
let [name, colors] = palette;
|
||||
if (!colors[0])
|
||||
return;
|
||||
|
||||
let subItem = item.menu.addAction(_(name || "Palette"), () => {
|
||||
item.label.set_text(_(name || "Palette"));
|
||||
this.area.currentPalette = palette;
|
||||
this._populateColorSubMenu();
|
||||
});
|
||||
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
||||
});
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
|
||||
menu.addMenuItem(item);
|
||||
return item;
|
||||
},
|
||||
|
||||
_addColorSubMenuItem: function(menu, icon) {
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(_("Color"), true);
|
||||
this.colorSubMenu = item.menu;
|
||||
item.icon.set_gicon(icon);
|
||||
item.icon.set_style(`color:${this.area.currentColor.to_string().slice(0, 7)};`);
|
||||
|
||||
item.menu.itemActivated = item.menu.close;
|
||||
|
||||
this._populateColorSubMenu();
|
||||
menu.addMenuItem(item);
|
||||
return item;
|
||||
},
|
||||
|
||||
_populateColorSubMenu: function() {
|
||||
this.colorSubMenu.removeAll();
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||
this.area.colors.forEach(color => {
|
||||
let text = String(color);
|
||||
let subItem = this.colorSubMenu.addAction(text, () => {
|
||||
this.area.currentColor = color;
|
||||
this.colorItem.icon.set_style(`color:${color.to_string().slice(0, 7)};`);
|
||||
});
|
||||
// Foreground color markup is not displayed since 3.36, use style instead but the transparency is lost.
|
||||
subItem.label.set_style(`color:${color.to_string().slice(0, 7)};`);
|
||||
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
||||
});
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
},
|
||||
|
||||
_addFontFamilySubMenuItem: function(menu, icon) {
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(DisplayStrings.getFontFamily(this.area.currentFontFamily), true);
|
||||
item.icon.set_gicon(icon);
|
||||
|
||||
item.menu.itemActivated = item.menu.close;
|
||||
|
||||
item.menu.openOld = item.menu.open;
|
||||
item.menu.open = (animate) => {
|
||||
if (!item.menu.isOpen && item.menu.isEmpty()) {
|
||||
this.area.fontFamilies.forEach(family => {
|
||||
let subItem = item.menu.addAction(_(family), () => {
|
||||
item.label.set_text(_(family));
|
||||
let subItem = item.menu.addAction(DisplayStrings.getFontFamily(family), () => {
|
||||
item.label.set_text(DisplayStrings.getFontFamily(family));
|
||||
this.area.currentFontFamily = family;
|
||||
});
|
||||
if (FONT_FAMILY_STYLE)
|
||||
|
|
@ -428,6 +532,34 @@ var DrawingMenu = new Lang.Class({
|
|||
menu.addMenuItem(item);
|
||||
},
|
||||
|
||||
_addImageSubMenuItem: function(menu, images) {
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem('', true);
|
||||
item.update = () => {
|
||||
item.label.set_text(this.area.currentImage.toString());
|
||||
item.icon.set_gicon(this.area.currentImage.gicon);
|
||||
};
|
||||
item.update();
|
||||
|
||||
item.menu.itemActivated = item.menu.close;
|
||||
|
||||
item.menu.openOld = item.menu.open;
|
||||
item.menu.open = (animate) => {
|
||||
if (!item.menu.isOpen && item.menu.isEmpty()) {
|
||||
Files.Images.getSorted().forEach(image => {
|
||||
let subItem = item.menu.addAction(image.toString(), () => {
|
||||
this.area.currentImage = image;
|
||||
item.update();
|
||||
}, image.thumbnailGicon || undefined);
|
||||
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
||||
});
|
||||
}
|
||||
item.menu.openOld();
|
||||
};
|
||||
|
||||
menu.addMenuItem(item);
|
||||
return item;
|
||||
},
|
||||
|
||||
_addDrawingNameItem: function(menu) {
|
||||
this.drawingNameMenuItem = new PopupMenu.PopupMenuItem('', { reactive: false, activate: false });
|
||||
this.drawingNameMenuItem.setSensitive(false);
|
||||
|
|
@ -436,24 +568,22 @@ var DrawingMenu = new Lang.Class({
|
|||
},
|
||||
|
||||
_updateDrawingNameMenuItem: function() {
|
||||
getActor(this.drawingNameMenuItem).visible = this.area.jsonName ? true : false;
|
||||
if (this.area.jsonName) {
|
||||
getActor(this.drawingNameMenuItem).visible = this.area.currentJson ? true : false;
|
||||
if (this.area.currentJson) {
|
||||
let prefix = this.area.drawingContentsHasChanged ? "* " : "";
|
||||
this.drawingNameMenuItem.label.set_text(`<i>${prefix}${this.area.jsonName}</i>`);
|
||||
this.drawingNameMenuItem.label.set_text(`<i>${prefix}${this.area.currentJson.name}</i>`);
|
||||
this.drawingNameMenuItem.label.get_clutter_text().set_use_markup(true);
|
||||
}
|
||||
},
|
||||
|
||||
_addOpenDrawingSubMenuItem: function(menu) {
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(_("Open drawing"), true);
|
||||
_addOpenDrawingSubMenuItem: function(menu, label, icon) {
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(label, true);
|
||||
this.openDrawingSubMenuItem = item;
|
||||
this.openDrawingSubMenu = item.menu;
|
||||
item.setSensitive(Boolean(Files.getJsons().length));
|
||||
item.icon.set_icon_name('document-open-symbolic');
|
||||
item.setSensitive(Boolean(Files.Jsons.getSorted().length));
|
||||
item.icon.set_icon_name(icon);
|
||||
|
||||
item.menu.itemActivated = () => {
|
||||
item.menu.close();
|
||||
};
|
||||
item.menu.itemActivated = item.menu.close;
|
||||
|
||||
item.menu.openOld = item.menu.open;
|
||||
item.menu.open = (animate) => {
|
||||
|
|
@ -467,13 +597,16 @@ var DrawingMenu = new Lang.Class({
|
|||
|
||||
_populateOpenDrawingSubMenu: function() {
|
||||
this.openDrawingSubMenu.removeAll();
|
||||
let jsons = Files.getJsons();
|
||||
jsons.forEach(json => {
|
||||
Files.Jsons.getSorted().forEach(json => {
|
||||
if (!json.gicon)
|
||||
json.addSvgContents(...this.area.getSvgContentsForJson(json));
|
||||
|
||||
let subItem = this.openDrawingSubMenu.addAction(`<i>${String(json)}</i>`, () => {
|
||||
this.area.loadJson(json.name);
|
||||
this.area.loadJson(json);
|
||||
this._updateDrawingNameMenuItem();
|
||||
this._updateSaveDrawingSubMenuItemSensitivity();
|
||||
});
|
||||
this._updateActionSensitivity();
|
||||
}, json.gicon);
|
||||
|
||||
subItem.label.get_clutter_text().set_use_markup(true);
|
||||
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
||||
|
||||
|
|
@ -483,10 +616,22 @@ var DrawingMenu = new Lang.Class({
|
|||
});
|
||||
getActor(subItem).add_child(expander);
|
||||
|
||||
let deleteButton = new St.Button({ style_class: 'draw-on-your-screen-menu-delete-button',
|
||||
let insertButton = new St.Button({ style_class: 'button draw-on-your-screen-menu-inline-button',
|
||||
child: new St.Icon({ icon_name: 'insert-image-symbolic',
|
||||
style_class: 'popup-menu-icon' }) });
|
||||
getActor(subItem).add_child(insertButton);
|
||||
|
||||
insertButton.connect('clicked', () => {
|
||||
this.area.currentImage = json.image;
|
||||
this.imageItem.update();
|
||||
this.area.currentTool = this.drawingTools.IMAGE;
|
||||
this.toolItem.update();
|
||||
this._updateSectionVisibility();
|
||||
});
|
||||
|
||||
let deleteButton = new St.Button({ style_class: 'button draw-on-your-screen-menu-inline-button draw-on-your-screen-menu-destructive-button',
|
||||
child: new St.Icon({ icon_name: 'edit-delete-symbolic',
|
||||
style_class: 'popup-menu-icon',
|
||||
x_align: Clutter.ActorAlign.END }) });
|
||||
style_class: 'popup-menu-icon' }) });
|
||||
getActor(subItem).add_child(deleteButton);
|
||||
|
||||
deleteButton.connect('clicked', () => {
|
||||
|
|
@ -499,16 +644,13 @@ var DrawingMenu = new Lang.Class({
|
|||
this.openDrawingSubMenuItem.setSensitive(!this.openDrawingSubMenu.isEmpty());
|
||||
},
|
||||
|
||||
_addSaveDrawingSubMenuItem: function(menu) {
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(_("Save drawing"), true);
|
||||
_addSaveDrawingSubMenuItem: function(menu, label, icon) {
|
||||
let item = new PopupMenu.PopupSubMenuMenuItem(label, true);
|
||||
this.saveDrawingSubMenuItem = item;
|
||||
this._updateSaveDrawingSubMenuItemSensitivity();
|
||||
this.saveDrawingSubMenu = item.menu;
|
||||
item.icon.set_icon_name('document-save-symbolic');
|
||||
item.icon.set_icon_name(icon);
|
||||
|
||||
item.menu.itemActivated = () => {
|
||||
item.menu.close();
|
||||
};
|
||||
item.menu.itemActivated = item.menu.close;
|
||||
|
||||
item.menu.openOld = item.menu.open;
|
||||
item.menu.open = (animate) => {
|
||||
|
|
@ -530,13 +672,14 @@ var DrawingMenu = new Lang.Class({
|
|||
|
||||
_populateSaveDrawingSubMenu: function() {
|
||||
this.saveDrawingSubMenu.removeAll();
|
||||
let saveEntry = new DrawingMenuEntry({ initialTextGetter: Files.getDateString,
|
||||
entryActivateCallback: (text) => {
|
||||
this.area.saveAsJsonWithName(text, this._onDrawingSaved.bind(this));
|
||||
this.saveDrawingSubMenu.toggle();
|
||||
},
|
||||
invalidStrings: [Me.metadata['persistent-file-name'], '/'],
|
||||
primaryIconName: 'insert-text' });
|
||||
let saveEntry = new Entry({ initialTextGetter: () => this.area.currentJson ? this.area.currentJson.name : "",
|
||||
hint_text: _("Type a name"),
|
||||
entryActivateCallback: (text) => {
|
||||
this.area.saveAsJsonWithName(text, this._onDrawingSaved.bind(this));
|
||||
this.saveDrawingSubMenu.toggle();
|
||||
},
|
||||
invalidStrings: [Me.metadata['persistent-file-name'], '/'],
|
||||
primaryIconName: 'insert-text' });
|
||||
this.saveDrawingSubMenu.addMenuItem(saveEntry.item);
|
||||
},
|
||||
|
||||
|
|
@ -568,8 +711,51 @@ const updateSubMenuAdjustment = function(itemActor) {
|
|||
adjustment.set_value(newScrollValue);
|
||||
};
|
||||
|
||||
// An action button that uses upstream dash item tooltips.
|
||||
const ActionButton = new Lang.Class({
|
||||
Name: 'DrawOnYourScreenDrawingMenuActionButton',
|
||||
Extends: St.Bin,
|
||||
_labelShowing: false,
|
||||
_resetHoverTimeoutId: 0,
|
||||
_showLabelTimeoutId: 0,
|
||||
showLabel: Dash.DashItemContainer.prototype.showLabel,
|
||||
hideLabel: Dash.DashItemContainer.prototype.hideLabel,
|
||||
_syncLabel: Dash.Dash.prototype._syncLabel,
|
||||
|
||||
_init: function(name, icon, callback, callbackAfter) {
|
||||
this._labelText = name;
|
||||
|
||||
let button = new St.Button({ track_hover: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
accessible_name: name,
|
||||
// use 'popup-menu' and 'popup-menu-item' style classes to provide theme colors
|
||||
//style_class: 'system-menu-action popup-menu-item popup-menu' });
|
||||
style_class: 'button draw-on-your-screen-menu-action-button' });
|
||||
button.child = new St.Icon(typeof icon == 'string' ? { icon_name: icon } : { gicon: icon });
|
||||
button.connect('clicked', () => {
|
||||
callback();
|
||||
if (callbackAfter)
|
||||
callbackAfter();
|
||||
});
|
||||
button.bind_property('reactive', button, 'can_focus', GObject.BindingFlags.DEFAULT);
|
||||
button.connect('notify::hover', () => this._syncLabel(this));
|
||||
|
||||
this.parent({ child: button, x_expand: true });
|
||||
},
|
||||
|
||||
get label() {
|
||||
if (!this._label) {
|
||||
this._label = new St.Label({ style_class: 'dash-label' });
|
||||
Main.layoutManager.uiGroup.add_actor(this._label);
|
||||
this.connect('destroy', () => this._label.destroy());
|
||||
}
|
||||
|
||||
return this._label;
|
||||
}
|
||||
});
|
||||
|
||||
// based on searchItem.js, https://github.com/leonardo-bartoli/gnome-shell-extension-Recents
|
||||
const DrawingMenuEntry = new Lang.Class({
|
||||
const Entry = new Lang.Class({
|
||||
Name: 'DrawOnYourScreenDrawingMenuEntry',
|
||||
|
||||
_init: function(params) {
|
||||
|
|
@ -582,6 +768,7 @@ const DrawingMenuEntry = new Lang.Class({
|
|||
this.itemActor = GS_VERSION < '3.33.0' ? this.item.actor : this.item;
|
||||
|
||||
this.entry = new St.Entry({
|
||||
hint_text: params.hint_text || "",
|
||||
style_class: 'search-entry draw-on-your-screen-menu-entry',
|
||||
track_hover: true,
|
||||
reactive: true,
|
||||
|
|
|
|||
|
|
@ -17,5 +17,5 @@
|
|||
"3.34",
|
||||
"3.36"
|
||||
],
|
||||
"version": 6.2
|
||||
"version": 6.3
|
||||
}
|
||||
|
|
|
|||
747
prefs.js
|
|
@ -1,4 +1,5 @@
|
|||
/* jslint esversion: 6 */
|
||||
/* exported init, buildPrefsWidget */
|
||||
|
||||
/*
|
||||
* Copyright 2019 Abakkk
|
||||
|
|
@ -20,104 +21,27 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 Lang = imports.lang;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = ExtensionUtils.getSettings && ExtensionUtils.initTranslations ? ExtensionUtils : Me.imports.convenience;
|
||||
const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext;
|
||||
const Shortcuts = Me.imports.shortcuts;
|
||||
const gettext = imports.gettext.domain(Me.metadata['gettext-domain']).gettext;
|
||||
const _ = function(string) {
|
||||
if (!string)
|
||||
return "";
|
||||
return gettext(string);
|
||||
};
|
||||
const _GTK = imports.gettext.domain('gtk30').gettext;
|
||||
|
||||
const GS_VERSION = Config.PACKAGE_VERSION;
|
||||
const MARGIN = 10;
|
||||
|
||||
var GLOBAL_KEYBINDINGS = {
|
||||
'toggle-drawing': "Enter/leave drawing mode",
|
||||
'toggle-modal': "Grab/ungrab keyboard and pointer",
|
||||
'erase-drawing': "Erase all drawings"
|
||||
};
|
||||
|
||||
var INTERNAL_KEYBINDINGS = {
|
||||
'undo': "Undo last brushstroke",
|
||||
'redo': "Redo last brushstroke",
|
||||
'delete-last-element' : "Erase last brushstroke",
|
||||
'smooth-last-element': "Smooth last brushstroke",
|
||||
'-separator-1': '',
|
||||
'select-none-shape': "Free drawing",
|
||||
'select-line-shape': "Select line",
|
||||
'select-ellipse-shape': "Select ellipse",
|
||||
'select-rectangle-shape': "Select rectangle",
|
||||
'select-polygon-shape': "Select polygon",
|
||||
'select-polyline-shape': "Select polyline",
|
||||
'select-text-shape': "Select text",
|
||||
'select-image-shape': "Select image",
|
||||
'select-move-tool': "Select move",
|
||||
'select-resize-tool': "Select resize",
|
||||
'select-mirror-tool': "Select mirror",
|
||||
'-separator-2': '',
|
||||
'switch-fill': "Toggle fill/outline",
|
||||
'switch-fill-rule': "Toggle fill rule",
|
||||
'-separator-3': '',
|
||||
'increment-line-width': "Increment line width",
|
||||
'decrement-line-width': "Decrement line width",
|
||||
'increment-line-width-more': "Increment line width even more",
|
||||
'decrement-line-width-more': "Decrement line width even more",
|
||||
'switch-linejoin': "Change linejoin",
|
||||
'switch-linecap': "Change linecap",
|
||||
'switch-dash': "Dashed line",
|
||||
'-separator-4': '',
|
||||
'switch-font-family': "Change font family",
|
||||
'reverse-switch-font-family': "Change font family (reverse)",
|
||||
'switch-font-weight': "Change font weight",
|
||||
'switch-font-style': "Change font style",
|
||||
'switch-text-alignment': "Toggle text alignment",
|
||||
'switch-image-file': "Change image file",
|
||||
'-separator-5': '',
|
||||
'toggle-panel-and-dock-visibility': "Hide panel and dock",
|
||||
'toggle-background': "Add a drawing background",
|
||||
'toggle-grid': "Add a grid overlay",
|
||||
'toggle-square-area': "Square drawing area",
|
||||
'-separator-6': '',
|
||||
'open-previous-json': "Open previous drawing",
|
||||
'open-next-json': "Open next drawing",
|
||||
'save-as-json': "Save drawing",
|
||||
'save-as-svg': "Save drawing as a SVG file",
|
||||
'open-user-stylesheet': "Edit style",
|
||||
'open-preferences': "Open preferences",
|
||||
'toggle-help': "Show help"
|
||||
};
|
||||
|
||||
if (GS_VERSION < "3.36")
|
||||
delete INTERNAL_KEYBINDINGS['open-preferences'];
|
||||
|
||||
function getKeyLabel(accel) {
|
||||
let [keyval, mods] = Gtk.accelerator_parse(accel);
|
||||
return Gtk.accelerator_get_label(keyval, mods);
|
||||
}
|
||||
|
||||
var OTHER_SHORTCUTS = [
|
||||
{ desc: "Draw", get shortcut() { return _("Left click"); } },
|
||||
{ desc: "Menu", get shortcut() { return _("Right click"); } },
|
||||
{ desc: "Toggle fill/outline", get shortcut() { return _("Center click"); } },
|
||||
{ desc: "Increment/decrement line width", get shortcut() { return _("Scroll"); } },
|
||||
{ desc: "Select color", get shortcut() { return _("%s … %s").format(getKeyLabel('<Primary>1'), getKeyLabel('<Primary>9')); } },
|
||||
{ desc: "Ignore pointer movement", get shortcut() { return _("%s held").format(getKeyLabel('space')); } },
|
||||
{ desc: "Leave", shortcut: getKeyLabel('Escape') },
|
||||
{ desc: "-separator-1", shortcut: "" },
|
||||
{ desc: "Select eraser <span alpha=\"50%\">(while starting drawing)</span>", shortcut: getKeyLabel('<Shift>') },
|
||||
{ desc: "Duplicate <span alpha=\"50%\">(while starting handling)</span>", shortcut: getKeyLabel('<Shift>') },
|
||||
{ desc: "Rotate rectangle, polygon, polyline", shortcut: getKeyLabel('<Primary>') },
|
||||
{ desc: "Extend circle to ellipse", shortcut: getKeyLabel('<Primary>') },
|
||||
{ desc: "Curve line", shortcut: getKeyLabel('<Primary>') },
|
||||
{ desc: "Smooth free drawing outline", shortcut: getKeyLabel('<Primary>') },
|
||||
{ desc: "Rotate <span alpha=\"50%\">(while moving)</span>", shortcut: getKeyLabel('<Primary>') },
|
||||
{ desc: "Stretch <span alpha=\"50%\">(while resizing)</span>", shortcut: getKeyLabel('<Primary>') },
|
||||
{ desc: "Inverse <span alpha=\"50%\">(while mirroring)</span>", shortcut: getKeyLabel('<Primary>') }
|
||||
];
|
||||
const ROWBOX_MARGIN_PARAMS = { margin_top: MARGIN / 2, margin_bottom: MARGIN / 2, margin_left: MARGIN, margin_right: MARGIN };
|
||||
|
||||
function init() {
|
||||
Convenience.initTranslations();
|
||||
|
|
@ -128,7 +52,6 @@ function buildPrefsWidget() {
|
|||
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();
|
||||
window.resize(720,500);
|
||||
let headerBar = window.get_titlebar();
|
||||
headerBar.custom_title = switcher;
|
||||
return false;
|
||||
|
|
@ -146,8 +69,13 @@ const TopStack = new GObject.Class({
|
|||
_init: function(params) {
|
||||
this.parent({ transition_type: 1, transition_duration: 500, expand: true });
|
||||
this.prefsPage = new PrefsPage();
|
||||
// Translators: "Preferences" page in preferences
|
||||
this.add_titled(this.prefsPage, 'prefs', _("Preferences"));
|
||||
this.drawingPage = new DrawingPage();
|
||||
// Translators: "Drawing" page in preferences
|
||||
this.add_titled(this.drawingPage, 'drawing', _("Drawing"));
|
||||
this.aboutPage = new AboutPage();
|
||||
// Translators: "About" page in preferences
|
||||
this.add_titled(this.aboutPage, 'about', _("About"));
|
||||
}
|
||||
});
|
||||
|
|
@ -158,14 +86,17 @@ const AboutPage = new GObject.Class({
|
|||
Extends: Gtk.ScrolledWindow,
|
||||
|
||||
_init: function(params) {
|
||||
this.parent();
|
||||
this.parent({ hscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||
|
||||
let vbox= new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, margin: MARGIN*3 });
|
||||
let vbox= new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, margin: MARGIN * 3 });
|
||||
this.add(vbox);
|
||||
|
||||
let name = "<b> " + _(Me.metadata.name) + "</b>";
|
||||
let version = _("Version %d").format(Me.metadata.version);
|
||||
let description = _(Me.metadata.description);
|
||||
// Translators: you are free to translate the extension name, that is displayed in About page, or not
|
||||
let name = "<b> " + _("Draw On You Screen") + "</b>";
|
||||
// Translators: version number in "About" page
|
||||
let version = _("Version %f").format(Me.metadata.version);
|
||||
// Translators: you are free to translate the extension description, that is displayed in About page, or not
|
||||
let description = _("Start drawing with Super+Alt+D and save your beautiful work by taking a screenshot");
|
||||
let link = "<span><a href=\"" + Me.metadata.url + "\">" + Me.metadata.url + "</a></span>";
|
||||
let licenceName = _GTK("GNU General Public License, version 2 or later");
|
||||
let licenceLink = "https://www.gnu.org/licenses/old-licenses/gpl-2.0.html";
|
||||
|
|
@ -176,7 +107,7 @@ const AboutPage = new GObject.Class({
|
|||
|
||||
vbox.add(aboutLabel);
|
||||
|
||||
let creditBox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, margin: 2*MARGIN });
|
||||
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: "<small>" + _GTK("Created by") + "</small>" });
|
||||
|
|
@ -187,6 +118,11 @@ const AboutPage = new GObject.Class({
|
|||
creditBox.pack_start(rightBox, true, true, 5);
|
||||
vbox.add(creditBox);
|
||||
|
||||
// Translators: add your name here or keep it empty, it will be displayed in about page, e.g.
|
||||
// msgstr ""
|
||||
// "translator1\n"
|
||||
// "<a href=\"mailto:translator2@mail.org\">translator2</a>\n"
|
||||
// "<a href=\"https://...\">translator3</a>"
|
||||
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);
|
||||
|
|
@ -196,7 +132,235 @@ const AboutPage = new GObject.Class({
|
|||
rightBox.pack_start(rightLabel, false, false, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const DrawingPage = new GObject.Class({
|
||||
Name: 'DrawOnYourScreenDrawingPage',
|
||||
GTypeName: 'DrawOnYourScreenDrawingPage',
|
||||
Extends: Gtk.ScrolledWindow,
|
||||
|
||||
_init: function(params) {
|
||||
this.parent({ hscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||
|
||||
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 palettesFrame = new Frame({ label: _("Palettes") });
|
||||
box.add(palettesFrame);
|
||||
|
||||
let palettesScrolledWindow = new Gtk.ScrolledWindow({ vscrollbar_policy: Gtk.PolicyType.NEVER, margin_top: MARGIN / 2, margin_bottom: MARGIN / 2 });
|
||||
palettesFrame.add(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());
|
||||
palettesScrolledWindow.add(this.palettesListBox);
|
||||
|
||||
this.settings.connect('changed::palettes', this._updatePalettes.bind(this));
|
||||
this._updatePalettes();
|
||||
|
||||
this.addBox = new Gtk.Box(ROWBOX_MARGIN_PARAMS);
|
||||
this.addBox.margin_bottom = MARGIN; // add space for the scrollbar
|
||||
let addButton = Gtk.Button.new_from_icon_name('list-add-symbolic', Gtk.IconSize.BUTTON);
|
||||
addButton.set_tooltip_text(_("Add a new palette"));
|
||||
this.addBox.pack_start(addButton, true, true, 4);
|
||||
addButton.connect('clicked', this._addNewPalette.bind(this));
|
||||
this.palettesListBox.add(this.addBox);
|
||||
this.addBox.get_parent().set_activatable(false);
|
||||
|
||||
let areaFrame = new Frame({ label: _("Area") });
|
||||
box.add(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);
|
||||
|
||||
let squareAreaRow = new PrefRow({ label: this.schema.get_key('square-area-size').get_summary() });
|
||||
let squareAreaAutoButton = new Gtk.CheckButton({ label: _("Auto"),
|
||||
name: this.schema.get_key('square-area-auto').get_summary(),
|
||||
tooltip_text: this.schema.get_key('square-area-auto').get_description() });
|
||||
let squareAreaSizeButton = new PixelSpinButton({ width_chars: 5, digits: 0, step: 1,
|
||||
range: this.schema.get_key('square-area-size').get_range(),
|
||||
name: this.schema.get_key('square-area-size').get_summary(),
|
||||
tooltip_text: this.schema.get_key('square-area-size').get_description() });
|
||||
this.settings.bind('square-area-auto', squareAreaAutoButton, 'active', 0);
|
||||
this.settings.bind('square-area-size', squareAreaSizeButton, 'value', 0);
|
||||
squareAreaAutoButton.bind_property('active', squareAreaSizeButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
||||
squareAreaRow.addWidget(squareAreaAutoButton);
|
||||
squareAreaRow.addWidget(squareAreaSizeButton);
|
||||
areaListBox.add(squareAreaRow);
|
||||
|
||||
let backgroundColorRow = new PrefRow({ label: this.schema.get_key('background-color').get_summary() });
|
||||
let backgroundColorButton = new ColorStringButton({ use_alpha: true, show_editor: true,
|
||||
name: this.schema.get_key('background-color').get_summary(),
|
||||
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);
|
||||
|
||||
let gridLineRow = new PrefRow({ label: _("Grid overlay line") });
|
||||
let gridLineAutoButton = new Gtk.CheckButton({ label: _("Auto"),
|
||||
name: this.schema.get_key('grid-line-auto').get_summary(),
|
||||
tooltip_text: this.schema.get_key('grid-line-auto').get_description() });
|
||||
let gridLineWidthButton = new PixelSpinButton({ width_chars: 5, digits: 1, step: 0.1,
|
||||
range: this.schema.get_key('grid-line-width').get_range(),
|
||||
name: this.schema.get_key('grid-line-width').get_summary(),
|
||||
tooltip_text: this.schema.get_key('grid-line-width').get_description() });
|
||||
let gridLineSpacingButton = new PixelSpinButton({ width_chars: 5, digits: 1, step: 1,
|
||||
range: this.schema.get_key('grid-line-spacing').get_range(),
|
||||
name: this.schema.get_key('grid-line-spacing').get_summary(),
|
||||
tooltip_text: this.schema.get_key('grid-line-spacing').get_description() });
|
||||
this.settings.bind('grid-line-auto', gridLineAutoButton, 'active', 0);
|
||||
this.settings.bind('grid-line-width', gridLineWidthButton, 'value', 0);
|
||||
this.settings.bind('grid-line-spacing', gridLineSpacingButton, 'value', 0);
|
||||
gridLineAutoButton.bind_property('active', gridLineWidthButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
||||
gridLineAutoButton.bind_property('active', gridLineSpacingButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
||||
gridLineRow.addWidget(gridLineAutoButton);
|
||||
gridLineRow.addWidget(gridLineWidthButton);
|
||||
gridLineRow.addWidget(gridLineSpacingButton);
|
||||
areaListBox.add(gridLineRow);
|
||||
|
||||
let gridColorRow = new PrefRow({ label: this.schema.get_key('grid-color').get_summary() });
|
||||
let gridColorButton = new ColorStringButton({ use_alpha: true, show_editor: true,
|
||||
name: this.schema.get_key('grid-color').get_summary(),
|
||||
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);
|
||||
|
||||
let toolsFrame = new Frame({ label: _("Tools") });
|
||||
box.add(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);
|
||||
|
||||
let dashArrayRow = new PrefRow({ label: _("Dash array") });
|
||||
let dashArrayAutoButton = new Gtk.CheckButton({ label: _("Auto"),
|
||||
name: this.schema.get_key('dash-array-auto').get_summary(),
|
||||
tooltip_text: this.schema.get_key('dash-array-auto').get_description() });
|
||||
let dashArrayOnButton = new PixelSpinButton({ width_chars: 5, digits: 1, step: 0.1,
|
||||
range: this.schema.get_key('dash-array-on').get_range(),
|
||||
name: this.schema.get_key('dash-array-on').get_summary(),
|
||||
tooltip_text: this.schema.get_key('dash-array-on').get_description() });
|
||||
let dashArrayOffButton = new PixelSpinButton({ width_chars: 5, digits: 1, step: 0.1,
|
||||
range: this.schema.get_key('dash-array-off').get_range(),
|
||||
name: this.schema.get_key('dash-array-off').get_summary(),
|
||||
tooltip_text: this.schema.get_key('dash-array-off').get_description() });
|
||||
this.settings.bind('dash-array-auto', dashArrayAutoButton, 'active', 0);
|
||||
this.settings.bind('dash-array-on', dashArrayOnButton, 'value', 0);
|
||||
this.settings.bind('dash-array-off', dashArrayOffButton, 'value', 0);
|
||||
dashArrayAutoButton.bind_property('active', dashArrayOnButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
||||
dashArrayAutoButton.bind_property('active', dashArrayOffButton, 'sensitive', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
||||
dashArrayRow.addWidget(dashArrayAutoButton);
|
||||
dashArrayRow.addWidget(dashArrayOnButton);
|
||||
dashArrayRow.addWidget(dashArrayOffButton);
|
||||
toolsListBox.add(dashArrayRow);
|
||||
|
||||
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,
|
||||
range: this.schema.get_key('dash-offset').get_range(),
|
||||
name: this.schema.get_key('dash-offset').get_summary(),
|
||||
tooltip_text: this.schema.get_key('dash-offset').get_description() });
|
||||
this.settings.bind('dash-offset', dashOffsetButton, 'value', 0);
|
||||
dashOffsetRow.addWidget(dashOffsetButton);
|
||||
toolsListBox.add(dashOffsetRow);
|
||||
|
||||
let imageLocationRow = new PrefRow({ label: this.schema.get_key('image-location').get_summary() });
|
||||
let imageLocationButton = new FileChooserButton({ action: Gtk.FileChooserAction.SELECT_FOLDER,
|
||||
name: this.schema.get_key('image-location').get_summary(),
|
||||
tooltip_text: this.schema.get_key('image-location').get_description() });
|
||||
this.settings.bind('image-location', imageLocationButton, 'location', 0);
|
||||
imageLocationRow.addWidget(imageLocationButton);
|
||||
toolsListBox.add(imageLocationRow);
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
_updatePalettes: function() {
|
||||
this.palettes = this.settings.get_value('palettes').deep_unpack();
|
||||
this.palettesListBox.get_children().filter(row=> row.get_child() != this.addBox)
|
||||
.slice(this.palettes.length)
|
||||
.forEach(row => this.palettesListBox.remove(row));
|
||||
let paletteBoxes = this.palettesListBox.get_children().map(row => row.get_child()).filter(child => child != this.addBox);
|
||||
|
||||
this.palettes.forEach((palette, paletteIndex) => {
|
||||
let [name, colors] = palette;
|
||||
let paletteBox;
|
||||
|
||||
if (paletteBoxes[paletteIndex]) {
|
||||
paletteBox = paletteBoxes[paletteIndex];
|
||||
let nameEntry = paletteBox.get_children()[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") });
|
||||
nameEntry.paletteNameChangedHandler = nameEntry.connect('changed', this._onPaletteNameChanged.bind(this, paletteIndex));
|
||||
let removeButton = Gtk.Button.new_from_icon_name('list-remove-symbolic', Gtk.IconSize.BUTTON);
|
||||
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);
|
||||
this.palettesListBox.insert(paletteBox, paletteIndex);
|
||||
paletteBox.get_parent().set_activatable(false);
|
||||
}
|
||||
|
||||
colors.splice(9);
|
||||
while (colors.length < 9)
|
||||
colors.push('transparent');
|
||||
|
||||
let colorsBox = paletteBox.get_children()[1];
|
||||
let colorButtons = colorsBox.get_children();
|
||||
colors.forEach((color, colorIndex) => {
|
||||
if (colorButtons[colorIndex]) {
|
||||
colorButtons[colorIndex].color_string = color;
|
||||
} else {
|
||||
let colorButton = new ColorStringButton({ color_string: color, 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);
|
||||
}
|
||||
});
|
||||
|
||||
paletteBox.show_all();
|
||||
});
|
||||
},
|
||||
|
||||
_savePalettes: function() {
|
||||
this.settings.set_value('palettes', new GLib.Variant('a(sas)', this.palettes));
|
||||
},
|
||||
|
||||
_onPaletteNameChanged: function(index, entry) {
|
||||
this.palettes[index][0] = entry.get_text();
|
||||
this._savePalettes();
|
||||
},
|
||||
|
||||
_onPaletteColorChanged: function(paletteIndex, colorIndex, colorButton) {
|
||||
this.palettes[paletteIndex][1][colorIndex] = colorButton.get_rgba().to_string();
|
||||
this._savePalettes();
|
||||
},
|
||||
|
||||
_addNewPalette: function() {
|
||||
let colors = Array(9).fill('Black');
|
||||
// Translators: default name of a new palette
|
||||
this.palettes.push([_("New palette"), colors]);
|
||||
this._savePalettes();
|
||||
},
|
||||
|
||||
_removePalette: function(paletteIndex) {
|
||||
this.palettes.splice(paletteIndex, 1);
|
||||
this._savePalettes();
|
||||
}
|
||||
});
|
||||
|
||||
const PrefsPage = new GObject.Class({
|
||||
|
|
@ -205,143 +369,276 @@ const PrefsPage = new GObject.Class({
|
|||
Extends: Gtk.ScrolledWindow,
|
||||
|
||||
_init: function(params) {
|
||||
this.parent();
|
||||
this.parent({ hscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||
|
||||
this.settings = Convenience.getSettings();
|
||||
let settings = Convenience.getSettings();
|
||||
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 });
|
||||
let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, margin: MARGIN * 3, spacing: 3 * MARGIN });
|
||||
this.add(box);
|
||||
|
||||
let globalFrame = new Gtk.Frame({ label_yalign: 1.0 });
|
||||
globalFrame.set_label_widget(new Gtk.Label({ margin_bottom: MARGIN/2, use_markup: true, label: "<b><big>" + _("Global") + "</big></b>" }));
|
||||
let globalFrame = new Frame({ label: _("Global") });
|
||||
box.add(globalFrame);
|
||||
|
||||
let listBox = new Gtk.ListBox({ selection_mode: 0, hexpand: true, margin_top: MARGIN/2, margin_bottom: MARGIN/2 });
|
||||
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);
|
||||
|
||||
let styleContext = listBox.get_style_context();
|
||||
styleContext.add_class('background');
|
||||
Shortcuts.GLOBAL_KEYBINDINGS.forEach((settingKeys, index) => {
|
||||
if (index)
|
||||
listBox.add(new Gtk.Box(ROWBOX_MARGIN_PARAMS));
|
||||
|
||||
let globalKeybindingsRow = new Gtk.ListBoxRow({ activatable: false });
|
||||
let globalKeybindingsWidget = new KeybindingsWidget(settingKeys, settings);
|
||||
globalKeybindingsRow.add(globalKeybindingsWidget);
|
||||
listBox.add(globalKeybindingsRow);
|
||||
});
|
||||
|
||||
let globalKeybindingsWidget = new KeybindingsWidget(GLOBAL_KEYBINDINGS, this.settings);
|
||||
globalKeybindingsWidget.margin = MARGIN;
|
||||
listBox.add(globalKeybindingsWidget);
|
||||
let persistentOverTogglesKey = schema.get_key('persistent-over-toggles');
|
||||
let persistentOverTogglesRow = new PrefRow({ label: persistentOverTogglesKey.get_summary(), desc: persistentOverTogglesKey.get_description() });
|
||||
let persistentOverTogglesSwitch = new Gtk.Switch();
|
||||
settings.bind('persistent-over-toggles', persistentOverTogglesSwitch, 'active', 0);
|
||||
persistentOverTogglesRow.addWidget(persistentOverTogglesSwitch, true);
|
||||
listBox.add(persistentOverTogglesRow);
|
||||
|
||||
let persistentBox = new Gtk.Box({ margin_top: MARGIN/2, margin_bottom: MARGIN/2, margin_left: MARGIN, margin_right: MARGIN });
|
||||
let persistentLabelBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||
let persistentLabel1 = new Gtk.Label({label: _("Persistent")});
|
||||
let persistentLabel2 = new Gtk.Label({ use_markup: true, halign: 1, wrap: true, xalign: 0, label: "<small>" + _("Persistent drawing through session restart") + "</small>" });
|
||||
persistentLabel1.set_halign(1);
|
||||
persistentLabel2.get_style_context().add_class('dim-label');
|
||||
persistentLabelBox.pack_start(persistentLabel1, true, true, 0);
|
||||
persistentLabelBox.pack_start(persistentLabel2, true, true, 0);
|
||||
let persistentSwitch = new Gtk.Switch({valign: 3});
|
||||
this.settings.bind('persistent-drawing', persistentSwitch, 'active', 0);
|
||||
persistentBox.pack_start(persistentLabelBox, true, true, 4);
|
||||
persistentBox.pack_start(persistentSwitch, false, false, 4);
|
||||
listBox.add(persistentBox);
|
||||
let persistentOverRestartsKey = schema.get_key('persistent-over-restarts');
|
||||
let persistentOverRestartsRow = new PrefRow({ label: persistentOverRestartsKey.get_summary(), desc: persistentOverRestartsKey.get_description() });
|
||||
let persistentOverRestartsSwitch = new Gtk.Switch();
|
||||
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);
|
||||
|
||||
let desktopBox = new Gtk.Box({ margin_top: MARGIN/2, margin_bottom: MARGIN/2, margin_left: MARGIN, margin_right: MARGIN });
|
||||
let desktopLabelBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||
let desktopLabel1 = new Gtk.Label({label: _("Drawing on the desktop")});
|
||||
let desktopLabel2 = new Gtk.Label({ use_markup: true, halign: 1, wrap: true, xalign: 0, label: "<small>" + _("<i>Draw On Your Screen</i> becomes <i>Draw On Your Desktop</i>") + "</small>" });
|
||||
desktopLabel1.set_halign(1);
|
||||
desktopLabel2.get_style_context().add_class('dim-label');
|
||||
desktopLabelBox.pack_start(desktopLabel1, true, true, 0);
|
||||
desktopLabelBox.pack_start(desktopLabel2, true, true, 0);
|
||||
let desktopSwitch = new Gtk.Switch({valign: 3});
|
||||
this.settings.bind('drawing-on-desktop', desktopSwitch, 'active', 0);
|
||||
desktopBox.pack_start(desktopLabelBox, true, true, 4);
|
||||
desktopBox.pack_start(desktopSwitch, false, false, 4);
|
||||
listBox.add(desktopBox);
|
||||
let desktopKey = schema.get_key('drawing-on-desktop');
|
||||
let desktopRow = new PrefRow({ label: desktopKey.get_summary(), desc: desktopKey.get_description() });
|
||||
let desktopSwitch = new Gtk.Switch();
|
||||
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);
|
||||
|
||||
let osdBox = new Gtk.Box({ margin_top: MARGIN/2, margin_bottom: MARGIN/2, margin_left: MARGIN, margin_right: MARGIN });
|
||||
let osdLabelBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||
let osdLabel1 = new Gtk.Label({label: _("Disable on-screen notifications")});
|
||||
osdLabel1.set_halign(1);
|
||||
osdLabelBox.pack_start(osdLabel1, true, true, 0);
|
||||
let osdSwitch = new Gtk.Switch({valign: 3});
|
||||
this.settings.bind('osd-disabled', osdSwitch, 'active', 0);
|
||||
osdBox.pack_start(osdLabelBox, true, true, 4);
|
||||
osdBox.pack_start(osdSwitch, false, false, 4);
|
||||
listBox.add(osdBox);
|
||||
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);
|
||||
|
||||
let indicatorBox = new Gtk.Box({ margin_top: MARGIN/2, margin_bottom: MARGIN/2, margin_left: MARGIN, margin_right: MARGIN });
|
||||
let indicatorLabelBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||
let indicatorLabel1 = new Gtk.Label({label: _("Disable panel indicator")});
|
||||
indicatorLabel1.set_halign(1);
|
||||
indicatorLabelBox.pack_start(indicatorLabel1, true, true, 0);
|
||||
let indicatorSwitch = new Gtk.Switch({valign: 3});
|
||||
this.settings.bind('indicator-disabled', indicatorSwitch, 'active', 0);
|
||||
indicatorBox.pack_start(indicatorLabelBox, true, true, 4);
|
||||
indicatorBox.pack_start(indicatorSwitch, false, false, 4);
|
||||
listBox.add(indicatorBox);
|
||||
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);
|
||||
|
||||
let children = listBox.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (children[i].activatable)
|
||||
children[i].set_activatable(false);
|
||||
}
|
||||
|
||||
let internalFrame = new Gtk.Frame({ margin_top: 3*MARGIN, label_yalign: 1.0 });
|
||||
internalFrame.set_label_widget(new Gtk.Label({ margin_bottom: MARGIN/2, use_markup: true, label: "<b><big>" + _("Internal") + " </big></b>" + _("(in drawing mode)") }));
|
||||
let internalFrame = new Frame({ label: _("Internal"), desc: _("In drawing mode") });
|
||||
box.add(internalFrame);
|
||||
|
||||
listBox = new Gtk.ListBox({ selection_mode: 0, hexpand: true, margin_top: MARGIN });
|
||||
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);
|
||||
|
||||
styleContext = listBox.get_style_context();
|
||||
styleContext.add_class('background');
|
||||
Shortcuts.OTHERS.forEach((pairs, index) => {
|
||||
if (index)
|
||||
listBox.add(new Gtk.Box(ROWBOX_MARGIN_PARAMS));
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
for (let i = 0; i < OTHER_SHORTCUTS.length; i++) {
|
||||
if (OTHER_SHORTCUTS[i].desc.indexOf('-separator-') != -1) {
|
||||
listBox.add(new Gtk.Box({ margin_top: MARGIN, margin_left: MARGIN, margin_right: MARGIN }));
|
||||
continue;
|
||||
listBox.add(new Gtk.Box(ROWBOX_MARGIN_PARAMS));
|
||||
|
||||
Shortcuts.INTERNAL_KEYBINDINGS.forEach((settingKeys, index) => {
|
||||
if (index)
|
||||
listBox.add(new Gtk.Box(ROWBOX_MARGIN_PARAMS));
|
||||
|
||||
let internalKeybindingsWidget = new KeybindingsWidget(settingKeys, internalShortcutSettings);
|
||||
listBox.add(internalKeybindingsWidget);
|
||||
});
|
||||
|
||||
listBox.get_children().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');
|
||||
resetButton.connect('clicked', () => {
|
||||
internalShortcutSettings.settings_schema.list_keys().forEach(key => internalShortcutSettings.reset(key));
|
||||
settings.settings_schema.list_keys().forEach(key => settings.reset(key));
|
||||
});
|
||||
box.add(resetButton);
|
||||
}
|
||||
});
|
||||
|
||||
const Frame = new GObject.Class({
|
||||
Name: 'DrawOnYourScreenFrame',
|
||||
GTypeName: 'DrawOnYourScreenFrame',
|
||||
Extends: Gtk.Frame,
|
||||
|
||||
_init: function(params) {
|
||||
let labelWidget = new Gtk.Label({ margin_bottom: MARGIN / 2, use_markup: true, label: `<b><big>${params.label}</big></b>` });
|
||||
this.parent({ label_yalign: 1.0, label_widget: labelWidget });
|
||||
|
||||
if (params.desc) {
|
||||
labelWidget.set_tooltip_text(params.desc);
|
||||
this.get_accessible().set_description(params.desc);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const PrefRow = new GObject.Class({
|
||||
Name: 'DrawOnYourScreenPrefRow',
|
||||
GTypeName: 'DrawOnYourScreenPrefRow',
|
||||
Extends: Gtk.ListBoxRow,
|
||||
|
||||
_init: function(params) {
|
||||
this.parent({ activatable: false });
|
||||
|
||||
let hbox = new Gtk.Box(ROWBOX_MARGIN_PARAMS);
|
||||
this.add(hbox);
|
||||
|
||||
let labelBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||
hbox.pack_start(labelBox, true, true, 4);
|
||||
|
||||
this.widgetBox = new Gtk.Box({ spacing: 4 });
|
||||
hbox.pack_start(this.widgetBox, false, false, 4);
|
||||
|
||||
this.label = new Gtk.Label({ use_markup: true, label: params.label, halign: Gtk.Align.START });
|
||||
labelBox.pack_start(this.label, true, true, 0);
|
||||
if (params.desc) {
|
||||
this.desc = new Gtk.Label({ use_markup: true, label: `<small>${params.desc}</small>`, halign: Gtk.Align.START, wrap: true, xalign: 0 });
|
||||
this.desc.get_style_context().add_class('dim-label');
|
||||
labelBox.pack_start(this.desc, true, true, 0);
|
||||
this.widgetBox.set_valign(Gtk.Align.START);
|
||||
}
|
||||
},
|
||||
|
||||
addWidget: function(widget, setRelationship) {
|
||||
this.widgetBox.add(widget);
|
||||
|
||||
if (widget.name)
|
||||
widget.get_accessible().set_name(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 (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());
|
||||
}
|
||||
let otherBox = new Gtk.Box({ margin_left: MARGIN, margin_right: MARGIN });
|
||||
let otherLabel = new Gtk.Label({ label: _(OTHER_SHORTCUTS[i].desc), use_markup: true });
|
||||
otherLabel.set_halign(1);
|
||||
let otherLabel2 = new Gtk.Label({ label: OTHER_SHORTCUTS[i].shortcut });
|
||||
otherBox.pack_start(otherLabel, true, true, 4);
|
||||
otherBox.pack_start(otherLabel2, false, false, 4);
|
||||
listBox.add(otherBox);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const PixelSpinButton = new GObject.Class({
|
||||
Name: 'DrawOnYourScreenPixelSpinButton',
|
||||
GTypeName: 'DrawOnYourScreenPixelSpinButton',
|
||||
Extends: Gtk.SpinButton,
|
||||
Properties: {
|
||||
'range': GObject.param_spec_variant('range', 'range', 'GSettings range',
|
||||
GLib.VariantType.new('(sv)'), null, GObject.ParamFlags.WRITABLE),
|
||||
|
||||
'step': GObject.ParamSpec.double('step', 'step', 'step increment',
|
||||
GObject.ParamFlags.WRITABLE,
|
||||
0, 1000, 1)
|
||||
},
|
||||
|
||||
set range(range) {
|
||||
let [type, variant] = range.deep_unpack();
|
||||
if (type == 'range') {
|
||||
let [min, max] = variant.deep_unpack();
|
||||
this.adjustment.set_lower(min);
|
||||
this.adjustment.set_upper(max);
|
||||
}
|
||||
},
|
||||
|
||||
set step(step) {
|
||||
this.adjustment.set_step_increment(step);
|
||||
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;
|
||||
},
|
||||
|
||||
// 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({
|
||||
Name: 'DrawOnYourScreenColorStringButton',
|
||||
GTypeName: 'DrawOnYourScreenColorStringButton',
|
||||
Extends: Gtk.ColorButton,
|
||||
Properties: {
|
||||
'color-string': GObject.ParamSpec.string('color-string', 'colorString', 'A string that describes the color',
|
||||
GObject.ParamFlags.READWRITE, 'black')
|
||||
},
|
||||
|
||||
get color_string() {
|
||||
return this._color_string || 'black';
|
||||
},
|
||||
|
||||
set color_string(colorString) {
|
||||
this._color_string = colorString;
|
||||
|
||||
let newRgba = new Gdk.RGBA();
|
||||
newRgba.parse(colorString);
|
||||
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);
|
||||
|
||||
if (!this.rgba.equal(oldRgba)) {
|
||||
this._color_string = this.rgba.to_string();
|
||||
this.notify('color-string');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const FileChooserButton = new GObject.Class({
|
||||
Name: 'DrawOnYourScreenFileChooserButton',
|
||||
GTypeName: 'DrawOnYourScreenFileChooserButton',
|
||||
Extends: Gtk.FileChooserButton,
|
||||
Properties: {
|
||||
'location': GObject.ParamSpec.string('location', 'location', 'location',
|
||||
GObject.ParamFlags.READWRITE, '')
|
||||
},
|
||||
|
||||
get location() {
|
||||
return this.get_file().get_path();
|
||||
},
|
||||
|
||||
set location(location) {
|
||||
if (!location) {
|
||||
this.unselect_all();
|
||||
if (this.get_file())
|
||||
this.set_file(Gio.File.new_for_path('aFileThatDoesNotExist'));
|
||||
return;
|
||||
}
|
||||
|
||||
let internalKeybindingsWidget = new KeybindingsWidget(INTERNAL_KEYBINDINGS, this.settings);
|
||||
internalKeybindingsWidget.margin = MARGIN;
|
||||
listBox.add(internalKeybindingsWidget);
|
||||
|
||||
let styleBox = new Gtk.Box({ margin: MARGIN });
|
||||
let styleLabel = new Gtk.Label({
|
||||
wrap: true,
|
||||
xalign: 0,
|
||||
use_markup: true,
|
||||
label: _("<b>Default</b> drawing style attributes (color palette, font, line, dash) are defined in an editable <b>css</b> file.\n" +
|
||||
"See <i>“%s”</i>.").format(_("Edit style"))
|
||||
});
|
||||
styleLabel.set_halign(1);
|
||||
styleLabel.get_style_context().add_class('dim-label');
|
||||
styleBox.pack_start(styleLabel, true, true, 4);
|
||||
listBox.add(styleBox);
|
||||
|
||||
let noteBox = new Gtk.Box({ margin: MARGIN });
|
||||
let noteLabel = new Gtk.Label({
|
||||
wrap: true,
|
||||
xalign: 0,
|
||||
use_markup: true,
|
||||
label: _("When you save elements made with <b>eraser</b> in a <b>SVG</b> file, " +
|
||||
"they are colored with background color, transparent if it is disabled.\n" +
|
||||
"See <i>“%s”</i> or edit the SVG file afterwards.").format(_("Add a drawing background"))
|
||||
});
|
||||
noteLabel.set_halign(1);
|
||||
noteLabel.get_style_context().add_class('dim-label');
|
||||
noteBox.pack_start(noteLabel, true, true, 4);
|
||||
listBox.add(noteBox);
|
||||
|
||||
children = listBox.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (children[i].activatable)
|
||||
children[i].set_activatable(false);
|
||||
}
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -351,11 +648,11 @@ const KeybindingsWidget = new GObject.Class({
|
|||
GTypeName: 'DrawOnYourScreenKeybindingsWidget',
|
||||
Extends: Gtk.Box,
|
||||
|
||||
_init: function(keybindings, settings) {
|
||||
this.parent();
|
||||
_init: function(settingKeys, settings) {
|
||||
this.parent(ROWBOX_MARGIN_PARAMS);
|
||||
this.set_orientation(Gtk.Orientation.VERTICAL);
|
||||
|
||||
this._keybindings = keybindings;
|
||||
this._settingKeys = settingKeys;
|
||||
this._settings = settings;
|
||||
|
||||
this._columns = {
|
||||
|
|
@ -436,17 +733,27 @@ const KeybindingsWidget = new GObject.Class({
|
|||
this.keybinding_column = keybinding_column;
|
||||
this.action_column = action_column;
|
||||
|
||||
this._settings.connect('changed', this._onSettingsChanged.bind(this));
|
||||
this._refresh();
|
||||
},
|
||||
|
||||
// Support the case where all the settings has been reset.
|
||||
_onSettingsChanged: function() {
|
||||
if (this._refreshTimeout)
|
||||
GLib.source_remove(this._refreshTimeout);
|
||||
|
||||
this._refreshTimeout = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||
this._refreshTimeout = 0;
|
||||
this._refresh();
|
||||
});
|
||||
},
|
||||
|
||||
_refresh: function() {
|
||||
this._store.clear();
|
||||
|
||||
for(let settings_key in this._keybindings) {
|
||||
if (settings_key.indexOf('-separator-') != -1)
|
||||
continue;
|
||||
this._settingKeys.forEach(settingKey => {
|
||||
let [key, mods] = Gtk.accelerator_parse(
|
||||
this._settings.get_strv(settings_key)[0]
|
||||
this._settings.get_strv(settingKey)[0] || ''
|
||||
);
|
||||
|
||||
let iter = this._store.append();
|
||||
|
|
@ -458,12 +765,12 @@ const KeybindingsWidget = new GObject.Class({
|
|||
this._columns.KEY
|
||||
],
|
||||
[
|
||||
settings_key,
|
||||
_(this._keybindings[settings_key]),
|
||||
settingKey,
|
||||
this._settings.settings_schema.get_key(settingKey).get_summary(),
|
||||
mods,
|
||||
key
|
||||
]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,290 +1,342 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<schemalist gettext-domain="gnome-shell-extensions">
|
||||
<schemalist gettext-domain="draw-on-your-screen">
|
||||
<schema path="/org/gnome/shell/extensions/draw-on-your-screen/" id="org.gnome.shell.extensions.draw-on-your-screen">
|
||||
<child name="drawing" schema="org.gnome.shell.extensions.draw-on-your-screen.drawing"/>
|
||||
<child name="internal-shortcuts" schema="org.gnome.shell.extensions.draw-on-your-screen.internal-shortcuts"/>
|
||||
<key type="b" name="drawing-on-desktop">
|
||||
<default>false</default>
|
||||
<summary>move drawing on desktop</summary>
|
||||
<description>move drawing on desktop</description>
|
||||
<summary>Drawing on the desktop</summary>
|
||||
<description><![CDATA[<i>Draw On Your Screen</i> becomes <i>Draw On Your Desktop</i>]]></description>
|
||||
</key>
|
||||
<key type="b" name="persistent-drawing">
|
||||
<default>false</default>
|
||||
<summary>persistent drawing</summary>
|
||||
<description>persistent drawing</description>
|
||||
</key>
|
||||
<key type="b" name="osd-disabled">
|
||||
<default>false</default>
|
||||
<summary>disable OSD notifications</summary>
|
||||
<description>disable on-screen notifications</description>
|
||||
<key type="as" name="erase-drawings">
|
||||
<default>["<Alt><Super>e"]</default>
|
||||
<summary>Erase all drawings</summary>
|
||||
</key>
|
||||
<key type="b" name="indicator-disabled">
|
||||
<default>false</default>
|
||||
<summary>disable panel indicator</summary>
|
||||
<description>disable panel indicator</description>
|
||||
<summary>Disable panel indicator</summary>
|
||||
</key>
|
||||
<key type="b" name="osd-disabled">
|
||||
<default>false</default>
|
||||
<summary>Disable on-screen notifications</summary>
|
||||
</key>
|
||||
<key type="b" name="persistent-over-toggles">
|
||||
<default>true</default>
|
||||
<summary>Persistent over toggles</summary>
|
||||
<description>Drawing remains when toggling drawing mode</description>
|
||||
</key>
|
||||
<key type="b" name="persistent-over-restarts">
|
||||
<default>false</default>
|
||||
<summary>Persistent over restarts</summary>
|
||||
<description>Drawing is automatically saved to a file</description>
|
||||
</key>
|
||||
<key type="as" name="toggle-drawing">
|
||||
<default>["<Alt><Super>d"]</default>
|
||||
<summary>toggle drawing</summary>
|
||||
<description>enter or leave drawing mode</description>
|
||||
<summary>Enter/leave drawing mode</summary>
|
||||
</key>
|
||||
<key type="as" name="toggle-modal">
|
||||
<default>["<Primary><Alt><Super>d"]</default>
|
||||
<summary>toggle modeless/modal</summary>
|
||||
<description>toggle modeless/modal</description>
|
||||
<!-- Translators: there is a similar text in GNOME Boxes (https://gitlab.gnome.org/GNOME/gnome-boxes/tree/master/po) -->
|
||||
<summary>Grab/ungrab keyboard and pointer</summary>
|
||||
<description></description>
|
||||
</key>
|
||||
<key type="as" name="erase-drawing">
|
||||
<default>["<Alt><Super>e"]</default>
|
||||
<summary>erase drawing</summary>
|
||||
<description>erase drawing</description>
|
||||
</schema>
|
||||
<schema path="/org/gnome/shell/extensions/draw-on-your-screen/drawing/" id="org.gnome.shell.extensions.draw-on-your-screen.drawing">
|
||||
<key type="s" name="background-color">
|
||||
<default>"#2e2e2e"</default>
|
||||
<summary>Background color</summary>
|
||||
<description>The color of the drawing area background</description>
|
||||
</key>
|
||||
<key type="as" name="undo">
|
||||
<default>["<Primary>z"]</default>
|
||||
<summary>undo</summary>
|
||||
<description>undo</description>
|
||||
<key type="b" name="dash-array-auto">
|
||||
<default>true</default>
|
||||
<summary>Automatic dash array</summary>
|
||||
<description>Compute the lengths from the line width</description>
|
||||
</key>
|
||||
<key type="as" name="redo">
|
||||
<default>["<Primary><Shift>z"]</default>
|
||||
<summary>redo</summary>
|
||||
<description>redo</description>
|
||||
<key type="d" name="dash-array-on">
|
||||
<range min="0.1" max="16384"/>
|
||||
<default>5</default>
|
||||
<summary>Dash array on</summary>
|
||||
<description>The dash length in pixels</description>
|
||||
</key>
|
||||
<key type="as" name="delete-last-element">
|
||||
<default>["Delete"]</default>
|
||||
<summary>delete last element</summary>
|
||||
<description>delete last element</description>
|
||||
<key type="d" name="dash-array-off">
|
||||
<range min="0.1" max="16384"/>
|
||||
<default>15</default>
|
||||
<summary>Dash array off</summary>
|
||||
<description>The gap between the dashes in pixels</description>
|
||||
</key>
|
||||
<key type="as" name="smooth-last-element">
|
||||
<default>["<Primary>equal"]</default>
|
||||
<summary>smooth last brushstroke</summary>
|
||||
<description>smooth last brushstroke</description>
|
||||
<key type="d" name="dash-offset">
|
||||
<range min="-16384" max="16384"/>
|
||||
<default>0</default>
|
||||
<summary>Dash offset</summary>
|
||||
<description>The dash offset in pixels</description>
|
||||
</key>
|
||||
<key type="as" name="toggle-background">
|
||||
<default>["<Primary>b"]</default>
|
||||
<summary>toggle drawing background</summary>
|
||||
<description>toggle drawing background</description>
|
||||
<key type="s" name="grid-color">
|
||||
<default>"Gray"</default>
|
||||
<summary>Grid overlay color</summary>
|
||||
<description>The color of the lines</description>
|
||||
</key>
|
||||
<key type="as" name="toggle-grid">
|
||||
<default>["<Primary>g"]</default>
|
||||
<summary>toggle grid overlay</summary>
|
||||
<description>toggle grid overlay</description>
|
||||
<key type="b" name="grid-line-auto">
|
||||
<default>true</default>
|
||||
<summary>Automatic grid overlay line</summary>
|
||||
<description>Compute the lengths from the screen size</description>
|
||||
</key>
|
||||
<key type="as" name="toggle-panel-and-dock-visibility">
|
||||
<default>["<Primary>h"]</default>
|
||||
<summary>hide or show panel and dock</summary>
|
||||
<description>hide or show panel and dock</description>
|
||||
<key type="u" name="grid-line-spacing">
|
||||
<range min="1" max="16384"/>
|
||||
<default>10</default>
|
||||
<summary>Grid overlay line spacing</summary>
|
||||
<description>The gap between lines in pixels</description>
|
||||
</key>
|
||||
<key type="as" name="toggle-square-area">
|
||||
<default>["<Primary>n"]</default>
|
||||
<summary>toggle square area</summary>
|
||||
<description>toggle square area</description>
|
||||
<key type="d" name="grid-line-width">
|
||||
<range min="0.1" max="10"/>
|
||||
<default>0.5</default>
|
||||
<summary>Grid overlay line width</summary>
|
||||
<description>The line width in pixels</description>
|
||||
</key>
|
||||
<key type="as" name="select-ellipse-shape">
|
||||
<default>["<Primary>e"]</default>
|
||||
<summary>select cercle</summary>
|
||||
<description>select a cercle</description>
|
||||
<key type="s" name="image-location">
|
||||
<default>""</default>
|
||||
<summary>Image location</summary>
|
||||
<description>The location of the directory in which the image tool picks</description>
|
||||
</key>
|
||||
<key type="as" name="select-rectangle-shape">
|
||||
<default>["<Primary>r"]</default>
|
||||
<summary>select rectangle</summary>
|
||||
<description>select rectangle</description>
|
||||
<key type="a(sas)" name="palettes">
|
||||
<default>
|
||||
[
|
||||
("Palette", ["HotPink","Cyan","yellow","Orangered","Chartreuse","DarkViolet","White","Gray","Black"]),
|
||||
("GNOME HIG lighter", ["rgb(153,193,241)","rgb(143,240,164)","rgb(249,240,107)","rgb(255,190,111)","rgb(246,97,81)","rgb(220,138,221)","rgb(205,171,143)","rgb(255,255,255)","rgb(119,118,123)"]),
|
||||
("GNOME HIG light", ["rgb(98,160,241)","rgb(87,227,137)","rgb(248,228,92)","rgb(255,163,72)","rgb(237,51,59)","rgb(192,97,203)","rgb(181,131,90)","rgb(246,245,244)","rgb(94,92,100)"]),
|
||||
("GNOME HIG normal", ["rgb(53,132,228)","rgb(51,209,122)","rgb(246,211,45)","rgb(255,120,0)","rgb(224,27,36)","rgb(145,65,172)","rgb(152,106,68)","rgb(222,221,218)","rgb(61,56,70)"]),
|
||||
("GNOME HIG dark", ["rgb(28,113,216)","rgb(46,194,126)","rgb(245,194,17)","rgb(230,97,0)","rgb(192,28,40)","rgb(129,61,156)","rgb(134,94,60)","rgb(192,191,188)","rgb(36,31,49)"]),
|
||||
("GNOME HIG darker", ["rgb(26,095,180)","rgb(38,162,105)","rgb(229,165,10)","rgb(198,70,0)","rgb(165,29,45)","rgb(97,53,131)","rgb(99,69,44)","rgb(154,153,150)","rgb(0,0,0)"])
|
||||
]
|
||||
</default>
|
||||
<summary>Color palettes</summary>
|
||||
<description>The palettes of drawing colors</description>
|
||||
</key>
|
||||
<key type="as" name="select-polygon-shape">
|
||||
<default>["<Primary>y"]</default>
|
||||
<summary>select polygon</summary>
|
||||
<description>select polygon</description>
|
||||
<key type="b" name="square-area-auto">
|
||||
<default>true</default>
|
||||
<summary>Automatic square area size</summary>
|
||||
<description>Compute the area size from the screen size</description>
|
||||
</key>
|
||||
<key type="as" name="select-polyline-shape">
|
||||
<default>["<Primary>u"]</default>
|
||||
<summary>select polyline</summary>
|
||||
<description>select polyline</description>
|
||||
</key>
|
||||
<key type="as" name="select-line-shape">
|
||||
<default>["<Primary>l"]</default>
|
||||
<summary>select line</summary>
|
||||
<description>select a line</description>
|
||||
</key>
|
||||
<key type="as" name="select-text-shape">
|
||||
<default>["<Primary>t"]</default>
|
||||
<summary>select text</summary>
|
||||
<description>select text</description>
|
||||
</key>
|
||||
<key type="as" name="select-image-shape">
|
||||
<default>["<Primary>i"]</default>
|
||||
<summary>select image</summary>
|
||||
<description>select image</description>
|
||||
</key>
|
||||
<key type="as" name="select-none-shape">
|
||||
<default>["<Primary>p"]</default>
|
||||
<summary>unselect shape (free drawing)</summary>
|
||||
<description>unselect shape (free drawing)</description>
|
||||
</key>
|
||||
<key type="as" name="select-move-tool">
|
||||
<default>["<Primary>m"]</default>
|
||||
<summary>select move tool</summary>
|
||||
<description>select move tool</description>
|
||||
</key>
|
||||
<key type="as" name="select-resize-tool">
|
||||
<default>["<Primary>x"]</default>
|
||||
<summary>select resize tool</summary>
|
||||
<description>select resize tool</description>
|
||||
</key>
|
||||
<key type="as" name="select-mirror-tool">
|
||||
<default>["<Primary>c"]</default>
|
||||
<summary>select mirror tool</summary>
|
||||
<description>select mirror tool</description>
|
||||
</key>
|
||||
<key type="as" name="increment-line-width">
|
||||
<default><![CDATA[['<Primary>KP_Add','<Primary><Shift>plus']]]></default>
|
||||
<summary>increment the line width</summary>
|
||||
<description>increment the line width</description>
|
||||
<key type="u" name="square-area-size">
|
||||
<range min="64" max="32768"/>
|
||||
<default>512</default>
|
||||
<summary>Square area size</summary>
|
||||
<description>The size of the area in pixels</description>
|
||||
</key>
|
||||
</schema>
|
||||
<schema path="/org/gnome/shell/extensions/draw-on-your-screen/internal-shortcuts/" id="org.gnome.shell.extensions.draw-on-your-screen.internal-shortcuts">
|
||||
<key type="as" name="decrement-line-width">
|
||||
<default><![CDATA[['<Primary>KP_Subtract','<Primary>minus','<Primary><Shift>minus']]]></default>
|
||||
<summary>decrement the line width</summary>
|
||||
<description>decrement the line width</description>
|
||||
</key>
|
||||
<key type="as" name="increment-line-width-more">
|
||||
<default>["<Primary><Shift>KP_Add"]</default>
|
||||
<summary>increment the line width even more</summary>
|
||||
<description>increment the line width even more</description>
|
||||
<summary>Decrement line width</summary>
|
||||
</key>
|
||||
<key type="as" name="decrement-line-width-more">
|
||||
<default>["<Primary><Shift>KP_Subtract"]</default>
|
||||
<summary>decrement the line width even more</summary>
|
||||
<description>decrement the line width even more</description>
|
||||
<summary>Decrement line width even more</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-linejoin">
|
||||
<default>["<Primary>j"]</default>
|
||||
<summary>switch linejoin</summary>
|
||||
<description>switch linejoin</description>
|
||||
<key type="as" name="delete-last-element">
|
||||
<default>["Delete"]</default>
|
||||
<summary>Erase last brushstroke</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-linecap">
|
||||
<default>["<Primary>k"]</default>
|
||||
<summary>switch linecap</summary>
|
||||
<description>switch linecap</description>
|
||||
<key type="as" name="export-to-svg">
|
||||
<default>["<Primary><Alt>s"]</default>
|
||||
<summary>Export drawing to a SVG file</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-fill-rule">
|
||||
<default><![CDATA[['<Primary>KP_Multiply','<Primary>asterisk','<Primary><Shift>asterisk']]]></default>
|
||||
<summary>switch fill rule</summary>
|
||||
<description>switch fill rule</description>
|
||||
<key type="as" name="increment-line-width">
|
||||
<default><![CDATA[['<Primary>KP_Add','<Primary><Shift>plus']]]></default>
|
||||
<summary>Increment line width</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-dash">
|
||||
<default>["<Primary>period"]</default>
|
||||
<summary>switch dash</summary>
|
||||
<description>switch dash</description>
|
||||
</key>
|
||||
<key type="as" name="switch-fill">
|
||||
<default>["<Primary>a"]</default>
|
||||
<summary>switch fill</summary>
|
||||
<description>switch fill</description>
|
||||
</key>
|
||||
<key type="as" name="select-color1">
|
||||
<default><![CDATA[['<Primary>KP_1','<Primary>1']]]></default>
|
||||
<summary>select color1</summary>
|
||||
<description>select color1</description>
|
||||
</key>
|
||||
<key type="as" name="select-color2">
|
||||
<default><![CDATA[['<Primary>KP_2','<Primary>2']]]></default>
|
||||
<summary>select color2</summary>
|
||||
<description>select color2</description>
|
||||
</key>
|
||||
<key type="as" name="select-color3">
|
||||
<default><![CDATA[['<Primary>KP_3','<Primary>3']]]></default>
|
||||
<summary>select color3</summary>
|
||||
<description>select color3</description>
|
||||
</key>
|
||||
<key type="as" name="select-color4">
|
||||
<default><![CDATA[['<Primary>KP_4','<Primary>4']]]></default>
|
||||
<summary>select color4</summary>
|
||||
<description>select color4</description>
|
||||
</key>
|
||||
<key type="as" name="select-color5">
|
||||
<default><![CDATA[['<Primary>KP_5','<Primary>5']]]></default>
|
||||
<summary>select color5</summary>
|
||||
<description>select color5</description>
|
||||
</key>
|
||||
<key type="as" name="select-color6">
|
||||
<default><![CDATA[['<Primary>KP_6','<Primary>6']]]></default>
|
||||
<summary>select color6</summary>
|
||||
<description>select color6</description>
|
||||
</key>
|
||||
<key type="as" name="select-color7">
|
||||
<default><![CDATA[['<Primary>KP_7','<Primary>7']]]></default>
|
||||
<summary>select color7</summary>
|
||||
<description>select color7</description>
|
||||
</key>
|
||||
<key type="as" name="select-color8">
|
||||
<default><![CDATA[['<Primary>KP_8','<Primary>8']]]></default>
|
||||
<summary>select color8</summary>
|
||||
<description>select color8</description>
|
||||
</key>
|
||||
<key type="as" name="select-color9">
|
||||
<default><![CDATA[['<Primary>KP_9','<Primary>9']]]></default>
|
||||
<summary>select color9</summary>
|
||||
<description>select color9</description>
|
||||
</key>
|
||||
<key type="as" name="switch-font-family">
|
||||
<default>["<Primary>f"]</default>
|
||||
<summary>switch font family</summary>
|
||||
<description>switch font family</description>
|
||||
</key>
|
||||
<key type="as" name="reverse-switch-font-family">
|
||||
<default>["<Primary><Shift>f"]</default>
|
||||
<summary>switch font family (reverse)</summary>
|
||||
<description>switch font family (reverse)</description>
|
||||
</key>
|
||||
<key type="as" name="switch-font-weight">
|
||||
<default>["<Primary>w"]</default>
|
||||
<summary>switch font weight</summary>
|
||||
<description>switch font weight</description>
|
||||
</key>
|
||||
<key type="as" name="switch-font-style">
|
||||
<default>["<Primary><Shift>w"]</default>
|
||||
<summary>switch font style</summary>
|
||||
<description>switch font style</description>
|
||||
</key>
|
||||
<key type="as" name="switch-text-alignment">
|
||||
<default>["<Primary><Shift>a"]</default>
|
||||
<summary>switch text alignment</summary>
|
||||
<description>switch text alignment</description>
|
||||
</key>
|
||||
<key type="as" name="switch-image-file">
|
||||
<default>["<Primary><Shift>i"]</default>
|
||||
<summary>switch image file</summary>
|
||||
<description>switch image file</description>
|
||||
</key>
|
||||
<key type="as" name="open-user-stylesheet">
|
||||
<default>["<Primary>o"]</default>
|
||||
<summary>open user stylesheet to edit style</summary>
|
||||
<description>open user stylesheet to edit style</description>
|
||||
</key>
|
||||
<key type="as" name="save-as-svg">
|
||||
<default>["<Primary><Shift>s"]</default>
|
||||
<summary>Save drawing as a svg file</summary>
|
||||
<description>Save drawing as a svg file</description>
|
||||
</key>
|
||||
<key type="as" name="save-as-json">
|
||||
<default>["<Primary>s"]</default>
|
||||
<summary>Save drawing as a json file</summary>
|
||||
<description>Save drawing as a json file</description>
|
||||
</key>
|
||||
<key type="as" name="open-previous-json">
|
||||
<default>["<Primary>Page_Down"]</default>
|
||||
<summary>Open previous json file</summary>
|
||||
<description>Open previous json file</description>
|
||||
<key type="as" name="increment-line-width-more">
|
||||
<default>["<Primary><Shift>KP_Add"]</default>
|
||||
<summary>Increment line width even more</summary>
|
||||
</key>
|
||||
<key type="as" name="open-next-json">
|
||||
<default>["<Primary>Page_Up"]</default>
|
||||
<summary>Open next json file</summary>
|
||||
<description>Open next json file</description>
|
||||
<default>["<Primary>o"]</default>
|
||||
<summary>Open next drawing</summary>
|
||||
</key>
|
||||
<key type="as" name="open-preferences">
|
||||
<default>["<Primary>comma"]</default>
|
||||
<summary>Open preferences</summary>
|
||||
<description>Open preferences</description>
|
||||
</key>
|
||||
<key type="as" name="open-previous-json">
|
||||
<default>["<Primary><Shift>o"]</default>
|
||||
<summary>Open previous drawing</summary>
|
||||
</key>
|
||||
<key type="as" name="paste-image-files">
|
||||
<default>["<Primary>v"]</default>
|
||||
<summary>Add images from the clipboard</summary>
|
||||
</key>
|
||||
<key type="as" name="redo">
|
||||
<default>["<Primary><Shift>z"]</default>
|
||||
<summary>Redo last brushstroke</summary>
|
||||
</key>
|
||||
<key type="as" name="save-as-json">
|
||||
<default>["<Primary>s"]</default>
|
||||
<summary>Save drawing</summary>
|
||||
</key>
|
||||
<key type="as" name="select-color1">
|
||||
<default><![CDATA[['<Primary>KP_1','<Primary>1']]]></default>
|
||||
<summary>Select color 1</summary>
|
||||
<description></description>
|
||||
</key>
|
||||
<key type="as" name="select-color2">
|
||||
<default><![CDATA[['<Primary>KP_2','<Primary>2']]]></default>
|
||||
<summary>Select color 2</summary>
|
||||
</key>
|
||||
<key type="as" name="select-color3">
|
||||
<default><![CDATA[['<Primary>KP_3','<Primary>3']]]></default>
|
||||
<summary>Select color 3</summary>
|
||||
</key>
|
||||
<key type="as" name="select-color4">
|
||||
<default><![CDATA[['<Primary>KP_4','<Primary>4']]]></default>
|
||||
<summary>Select color 4</summary>
|
||||
</key>
|
||||
<key type="as" name="select-color5">
|
||||
<default><![CDATA[['<Primary>KP_5','<Primary>5']]]></default>
|
||||
<summary>Select color 5</summary>
|
||||
</key>
|
||||
<key type="as" name="select-color6">
|
||||
<default><![CDATA[['<Primary>KP_6','<Primary>6']]]></default>
|
||||
<summary>Select color 6</summary>
|
||||
</key>
|
||||
<key type="as" name="select-color7">
|
||||
<default><![CDATA[['<Primary>KP_7','<Primary>7']]]></default>
|
||||
<summary>Select color 7</summary>
|
||||
</key>
|
||||
<key type="as" name="select-color8">
|
||||
<default><![CDATA[['<Primary>KP_8','<Primary>8']]]></default>
|
||||
<summary>Select color 8</summary>
|
||||
</key>
|
||||
<key type="as" name="select-color9">
|
||||
<default><![CDATA[['<Primary>KP_9','<Primary>9']]]></default>
|
||||
<summary>Select color 9</summary>
|
||||
</key>
|
||||
<key type="as" name="select-ellipse-shape">
|
||||
<default>["<Primary>e"]</default>
|
||||
<summary>Select ellipse tool</summary>
|
||||
</key>
|
||||
<key type="as" name="select-image-shape">
|
||||
<default>["<Primary>i"]</default>
|
||||
<summary>Select image tool</summary>
|
||||
</key>
|
||||
<key type="as" name="select-line-shape">
|
||||
<default>["<Primary>l"]</default>
|
||||
<summary>Select line tool</summary>
|
||||
</key>
|
||||
<key type="as" name="select-mirror-tool">
|
||||
<default>["<Primary>c"]</default>
|
||||
<summary>Select mirror tool</summary>
|
||||
</key>
|
||||
<key type="as" name="select-move-tool">
|
||||
<default>["<Primary>m"]</default>
|
||||
<summary>Select move tool</summary>
|
||||
</key>
|
||||
<key type="as" name="select-none-shape">
|
||||
<default>["<Primary>p"]</default>
|
||||
<summary>Select free drawing</summary>
|
||||
</key>
|
||||
<key type="as" name="select-polygon-shape">
|
||||
<default>["<Primary>y"]</default>
|
||||
<summary>Select polygon tool</summary>
|
||||
</key>
|
||||
<key type="as" name="select-polyline-shape">
|
||||
<default>["<Primary>u"]</default>
|
||||
<summary>Select polyline tool</summary>
|
||||
</key>
|
||||
<key type="as" name="select-rectangle-shape">
|
||||
<default>["<Primary>r"]</default>
|
||||
<summary>Select rectangle tool</summary>
|
||||
</key>
|
||||
<key type="as" name="select-resize-tool">
|
||||
<default>["<Primary>x"]</default>
|
||||
<summary>Select resize tool</summary>
|
||||
</key>
|
||||
<key type="as" name="select-text-shape">
|
||||
<default>["<Primary>t"]</default>
|
||||
<summary>Select text tool</summary>
|
||||
</key>
|
||||
<key type="as" name="smooth-last-element">
|
||||
<default>["<Primary>equal"]</default>
|
||||
<summary>Smooth last brushstroke</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-color-palette">
|
||||
<default>["<Primary>KP_Divide","<Primary>slash"]</default>
|
||||
<summary>Change color palette</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-color-palette-reverse">
|
||||
<default>["<Primary><Shift>KP_Divide","<Primary><Shift>slash"]</default>
|
||||
<summary>Change color palette (reverse)</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-dash">
|
||||
<default>["<Primary>period"]</default>
|
||||
<summary>Dashed line</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-fill">
|
||||
<default>["<Primary>a"]</default>
|
||||
<summary>Toggle fill/outline</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-fill-rule">
|
||||
<default><![CDATA[['<Primary>KP_Multiply','<Primary>asterisk','<Primary><Shift>asterisk']]]></default>
|
||||
<summary>Toggle fill rule</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-font-family">
|
||||
<default>["<Primary>f"]</default>
|
||||
<summary>Change font family</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-font-family-reverse">
|
||||
<default>["<Primary><Shift>f"]</default>
|
||||
<summary>Change font family (reverse)</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-font-style">
|
||||
<default>["<Primary><Alt>w"]</default>
|
||||
<summary>Change font style</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-font-weight">
|
||||
<default>["<Primary>w"]</default>
|
||||
<summary>Change font weight</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-image-file">
|
||||
<default>["<Primary><Alt>i"]</default>
|
||||
<summary>Change image</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-image-file-reverse">
|
||||
<default>["<Primary><Alt><Shift>i"]</default>
|
||||
<summary>Change image (reverse)</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-linecap">
|
||||
<default>["<Primary>k"]</default>
|
||||
<summary>Change linecap</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-linejoin">
|
||||
<default>["<Primary>j"]</default>
|
||||
<summary>Change linejoin</summary>
|
||||
</key>
|
||||
<key type="as" name="switch-text-alignment">
|
||||
<default>["<Primary><Alt>a"]</default>
|
||||
<summary>Toggle text alignment</summary>
|
||||
</key>
|
||||
<key type="as" name="toggle-background">
|
||||
<default>["<Primary>b"]</default>
|
||||
<summary>Add a drawing background</summary>
|
||||
</key>
|
||||
<key type="as" name="toggle-grid">
|
||||
<default>["<Primary>g"]</default>
|
||||
<summary>Add a grid overlay</summary>
|
||||
</key>
|
||||
<key type="as" name="toggle-help">
|
||||
<default>["<Primary>F1"]</default>
|
||||
<summary>toggle help</summary>
|
||||
<description>toggle help</description>
|
||||
<summary>Show help</summary>
|
||||
</key>
|
||||
<key type="as" name="toggle-panel-and-dock-visibility">
|
||||
<default>["<Primary>h"]</default>
|
||||
<summary>Hide panel and dock</summary>
|
||||
</key>
|
||||
<key type="as" name="toggle-square-area">
|
||||
<default>["<Primary>n"]</default>
|
||||
<!-- Translators: It is an action: "Make the drawing area a square" -->
|
||||
<summary>Square drawing area</summary>
|
||||
</key>
|
||||
<key type="as" name="undo">
|
||||
<default>["<Primary>z"]</default>
|
||||
<summary>Undo last brushstroke</summary>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
/* jslint esversion: 6 */
|
||||
/* exported GLOBAL_KEYBINDINGS, INTERNAL_KEYBINDINGS, OTHERS */
|
||||
|
||||
/*
|
||||
* Copyright 2019 Abakkk
|
||||
*
|
||||
* This file is part of DrawOnYourScreen, a drawing extension for GNOME Shell.
|
||||
* https://framagit.org/abakkk/DrawOnYourScreen
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const Gtk = imports.gi.Gtk;
|
||||
|
||||
const GS_VERSION = imports.misc.config.PACKAGE_VERSION;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const Convenience = ExtensionUtils.getSettings && ExtensionUtils.initTranslations ? ExtensionUtils : Me.imports.convenience;
|
||||
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);
|
||||
return Gtk.accelerator_get_label(keyval, mods);
|
||||
};
|
||||
|
||||
// The setting keys of the "org.gnome.shell.extensions.draw-on-your-screen" schema.
|
||||
var GLOBAL_KEYBINDINGS = [
|
||||
['toggle-drawing', 'toggle-modal', 'erase-drawings'],
|
||||
];
|
||||
|
||||
// The setting keys of the "org.gnome.shell.extensions.draw-on-your-screen.internal-shortcuts" schema.
|
||||
var INTERNAL_KEYBINDINGS = [
|
||||
['undo', 'redo', 'delete-last-element', 'smooth-last-element'],
|
||||
['select-none-shape', 'select-line-shape', 'select-ellipse-shape', 'select-rectangle-shape', 'select-polygon-shape', 'select-polyline-shape',
|
||||
'select-text-shape', 'select-image-shape', 'select-move-tool', 'select-resize-tool', 'select-mirror-tool'],
|
||||
['switch-fill', 'switch-fill-rule', 'switch-color-palette', 'switch-color-palette-reverse'],
|
||||
['increment-line-width', 'increment-line-width-more', 'decrement-line-width', 'decrement-line-width-more',
|
||||
'switch-linejoin', 'switch-linecap', 'switch-dash'],
|
||||
['switch-font-family', 'switch-font-family-reverse', 'switch-font-weight', 'switch-font-style', 'switch-text-alignment'],
|
||||
['switch-image-file', 'switch-image-file-reverse', 'paste-image-files'],
|
||||
['toggle-panel-and-dock-visibility', 'toggle-background', 'toggle-grid', 'toggle-square-area'],
|
||||
['open-next-json', 'open-previous-json', 'save-as-json', 'export-to-svg', 'open-preferences', 'toggle-help'],
|
||||
];
|
||||
|
||||
if (GS_VERSION < '3.36') {
|
||||
// Remove 'open-preferences' keybinding.
|
||||
INTERNAL_KEYBINDINGS.forEach(settingKeys => {
|
||||
let index = settingKeys.indexOf('open-preferences');
|
||||
if (index != -1)
|
||||
settingKeys.splice(index, 1);
|
||||
});
|
||||
}
|
||||
|
||||
const getOthers = function() {
|
||||
return [
|
||||
[
|
||||
[_("Draw"), _("Left click")],
|
||||
[_("Menu"), _("Right click")],
|
||||
[internalShortcutsSchema.get_key('switch-fill').get_summary(), _("Center click")],
|
||||
[_("Increment/decrement line width"), _("Scroll")],
|
||||
// Translators: %s are key labels (Ctrl+F1 and Ctrl+F9)
|
||||
[_("Select color"), _("%s … %s").format(getKeyLabel('<Primary>1'), getKeyLabel('<Primary>9'))],
|
||||
// Translators: %s is a key label
|
||||
[_("Ignore pointer movement"), _("%s held").format(getKeyLabel('space'))],
|
||||
[_("Leave"), getKeyLabel('Escape')],
|
||||
], [
|
||||
[_("Select eraser <span alpha=\"50%\">(while starting drawing)</span>"), getKeyLabel('<Shift>')],
|
||||
[_("Duplicate <span alpha=\"50%\">(while starting handling)</span>"), getKeyLabel('<Shift>')],
|
||||
[_("Rotate rectangle, polygon, polyline"), getKeyLabel('<Primary>')],
|
||||
[_("Extend circle to ellipse"), getKeyLabel('<Primary>')],
|
||||
[_("Curve line"), getKeyLabel('<Primary>')],
|
||||
[_("Smooth free drawing outline"), getKeyLabel('<Primary>')],
|
||||
[_("Unlock image ratio"), getKeyLabel('<Primary>')],
|
||||
[_("Rotate <span alpha=\"50%\">(while moving)</span>"), getKeyLabel('<Primary>')],
|
||||
[_("Stretch <span alpha=\"50%\">(while resizing)</span>"), getKeyLabel('<Primary>')],
|
||||
[_("Inverse <span alpha=\"50%\">(while mirroring)</span>"), getKeyLabel('<Primary>')],
|
||||
],
|
||||
];
|
||||
};
|
||||
|
||||
let _OTHERS;
|
||||
// Equivalent to "var OTHERS = [[ ... ]]", but as a getter so the translations are got after the initTranslations call.
|
||||
// 'this' is the module.
|
||||
Object.defineProperty(this, 'OTHERS', {
|
||||
get: function() {
|
||||
if (!_OTHERS)
|
||||
_OTHERS = getOthers();
|
||||
return _OTHERS;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
@import "./data/default.css";
|
||||
|
||||
/* The following styles don't affect the drawing */
|
||||
|
||||
/* square area */
|
||||
|
||||
.draw-on-your-screen-square-area {
|
||||
|
|
@ -48,7 +44,7 @@
|
|||
padding-bottom: .3em;
|
||||
}
|
||||
|
||||
.draw-on-your-screen-menu .popup-menu-icon {
|
||||
.draw-on-your-screen-menu .popup-menu-item > .popup-menu-icon {
|
||||
icon-size: 1em; /* default: 1.09 */
|
||||
padding-top: 0.03em;
|
||||
}
|
||||
|
|
@ -80,23 +76,20 @@
|
|||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* system-menu-action: from GS 3.34- */
|
||||
.draw-on-your-screen-menu .system-menu-action {
|
||||
min-width: 0;
|
||||
border: none;
|
||||
border-radius: 32px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
.draw-on-your-screen-menu-destructive-button:hover {
|
||||
color: #e01b24; /* upstream destructive color, light: #e01b24, dark: #b2161d */
|
||||
}
|
||||
|
||||
.draw-on-your-screen-menu .system-menu-action:hover,
|
||||
.draw-on-your-screen-menu .system-menu-action:focus {
|
||||
border: none;
|
||||
/* override .button upstream style class */
|
||||
.draw-on-your-screen-menu-action-button {
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
border-radius: 32px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.draw-on-your-screen-menu .system-menu-action > StIcon {
|
||||
icon-size: 16px;
|
||||
.draw-on-your-screen-menu-action-button > StIcon {
|
||||
icon-size: 1em;
|
||||
}
|
||||
|
||||
.draw-on-your-screen-menu-slider-label {
|
||||
|
|
@ -124,8 +117,13 @@
|
|||
padding: 0.35em 0.57em;
|
||||
}
|
||||
|
||||
.draw-on-your-screen-menu-delete-button:hover {
|
||||
color: #f57900;
|
||||
/* override .button upstream style class */
|
||||
.draw-on-your-screen-menu-inline-button {
|
||||
min-height: 1px;
|
||||
padding: 2px 4px; /* default 3px 24px */
|
||||
}
|
||||
|
||||
|
||||
|
||||
.draw-on-your-screen-menu-inline-button .popup-menu-icon {
|
||||
icon-size: 0.85em; /* default 1.09 */
|
||||
}
|
||||
|
||||
|
|
|
|||