Merge branch 'dev' into 'master'
v8.1 Closes #46 and #45 See merge request abakkk/DrawOnYourScreen!18
This commit is contained in:
commit
b3fffc193e
7
NEWS
7
NEWS
|
|
@ -1,3 +1,10 @@
|
|||
v8.1 - October 2020
|
||||
===================
|
||||
|
||||
* Fix unsanitized GType names #46
|
||||
* Quick free drawings are smoother #45
|
||||
* Transformations are undoable/redoable
|
||||
|
||||
v8 - September 2020
|
||||
===================
|
||||
|
||||
|
|
|
|||
157
area.js
157
area.js
|
|
@ -47,10 +47,12 @@ const pgettext = imports.gettext.domain(Me.metadata['gettext-domain']).pgettext;
|
|||
|
||||
const CAIRO_DEBUG_EXTENDS = false;
|
||||
const SVG_DEBUG_EXTENDS = false;
|
||||
const MOTION_TIME = 1; // ms, time accuracy for free drawing, max is about 33 ms. The lower it is, the smoother the drawing is.
|
||||
const TEXT_CURSOR_TIME = 600; // ms
|
||||
const ELEMENT_GRABBER_TIME = 80; // ms, default is about 16 ms
|
||||
const GRID_TILES_HORIZONTAL_NUMBER = 30;
|
||||
const COLOR_PICKER_EXTENSION_UUID = 'color-picker@tuberry';
|
||||
const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_');
|
||||
|
||||
const { Shapes, Transformations } = Elements;
|
||||
const { DisplayStrings } = Menu;
|
||||
|
|
@ -85,7 +87,7 @@ const getColorFromString = function(string, fallback) {
|
|||
// It creates and manages a DrawingElement for each "brushstroke".
|
||||
// It handles pointer/mouse/(touch?) events and some keyboard events.
|
||||
var DrawingArea = new Lang.Class({
|
||||
Name: `${Me.uuid}.DrawingArea`,
|
||||
Name: `${UUID}-DrawingArea`,
|
||||
Extends: St.DrawingArea,
|
||||
Signals: { 'show-osd': { param_types: [Gio.Icon.$gtype, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_DOUBLE, GObject.TYPE_BOOLEAN] },
|
||||
'update-action-mode': {},
|
||||
|
|
@ -393,8 +395,14 @@ var DrawingArea = new Lang.Class({
|
|||
},
|
||||
|
||||
_onStageKeyPressed: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_space)
|
||||
if (event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
if (this.helper.visible)
|
||||
this.toggleHelp();
|
||||
else
|
||||
this.emit('leave-drawing-mode');
|
||||
} else if (event.get_key_symbol() == Clutter.KEY_space) {
|
||||
this.spaceKeyPressed = true;
|
||||
}
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
|
@ -407,38 +415,28 @@ var DrawingArea = new Lang.Class({
|
|||
},
|
||||
|
||||
_onKeyPressed: function(actor, event) {
|
||||
if (this.currentElement && this.currentElement.shape == Shapes.LINE) {
|
||||
if (event.get_key_symbol() == Clutter.KEY_Return ||
|
||||
event.get_key_symbol() == Clutter.KEY_KP_Enter ||
|
||||
event.get_key_symbol() == Clutter.KEY_Control_L) {
|
||||
if (this.currentElement.points.length == 2)
|
||||
// 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();
|
||||
return Clutter.EVENT_STOP;
|
||||
} else {
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
if (this.currentElement && this.currentElement.shape == Shapes.LINE &&
|
||||
(event.get_key_symbol() == Clutter.KEY_Return ||
|
||||
event.get_key_symbol() == Clutter.KEY_KP_Enter ||
|
||||
event.get_key_symbol() == Clutter.KEY_Control_L)) {
|
||||
|
||||
if (this.currentElement.points.length == 2)
|
||||
// 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();
|
||||
return Clutter.EVENT_STOP;
|
||||
} else if (this.currentElement &&
|
||||
(this.currentElement.shape == Shapes.POLYGON || this.currentElement.shape == Shapes.POLYLINE) &&
|
||||
(event.get_key_symbol() == Clutter.KEY_Return || event.get_key_symbol() == Clutter.KEY_KP_Enter)) {
|
||||
|
||||
this.currentElement.addPoint();
|
||||
return Clutter.EVENT_STOP;
|
||||
|
||||
} else if (event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
if (this.helper.visible)
|
||||
this.toggleHelp();
|
||||
else
|
||||
this.emit('leave-drawing-mode');
|
||||
return Clutter.EVENT_STOP;
|
||||
|
||||
} else {
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
},
|
||||
|
||||
_onScroll: function(actor, event) {
|
||||
|
|
@ -535,16 +533,17 @@ var DrawingArea = new Lang.Class({
|
|||
this.grabbedElement = copy;
|
||||
}
|
||||
|
||||
let undoable = !duplicate;
|
||||
|
||||
if (this.currentTool == Manipulations.MOVE)
|
||||
this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformations.ROTATION : Transformations.TRANSLATION);
|
||||
this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformations.ROTATION : Transformations.TRANSLATION, undoable);
|
||||
else if (this.currentTool == Manipulations.RESIZE)
|
||||
this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformations.STRETCH : Transformations.SCALE_PRESERVE);
|
||||
this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformations.STRETCH : Transformations.SCALE_PRESERVE, undoable);
|
||||
else if (this.currentTool == Manipulations.MIRROR) {
|
||||
this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformations.INVERSION : Transformations.REFLECTION);
|
||||
this.grabbedElement.startTransformation(startX, startY, controlPressed ? Transformations.INVERSION : Transformations.REFLECTION, undoable);
|
||||
this._redisplay();
|
||||
}
|
||||
|
||||
|
||||
this.motionHandler = this.connect('motion-event', (actor, event) => {
|
||||
if (this.spaceKeyPressed)
|
||||
return;
|
||||
|
|
@ -559,28 +558,30 @@ var DrawingArea = new Lang.Class({
|
|||
},
|
||||
|
||||
_updateTransforming: function(x, y, controlPressed) {
|
||||
let undoable = this.grabbedElement.lastTransformation.undoable || false;
|
||||
|
||||
if (controlPressed && this.grabbedElement.lastTransformation.type == Transformations.TRANSLATION) {
|
||||
this.grabbedElement.stopTransformation();
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.ROTATION);
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.ROTATION, undoable);
|
||||
} else if (!controlPressed && this.grabbedElement.lastTransformation.type == Transformations.ROTATION) {
|
||||
this.grabbedElement.stopTransformation();
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.TRANSLATION);
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.TRANSLATION, undoable);
|
||||
}
|
||||
|
||||
if (controlPressed && this.grabbedElement.lastTransformation.type == Transformations.SCALE_PRESERVE) {
|
||||
this.grabbedElement.stopTransformation();
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.STRETCH);
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.STRETCH, undoable);
|
||||
} else if (!controlPressed && this.grabbedElement.lastTransformation.type == Transformations.STRETCH) {
|
||||
this.grabbedElement.stopTransformation();
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.SCALE_PRESERVE);
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.SCALE_PRESERVE, undoable);
|
||||
}
|
||||
|
||||
if (controlPressed && this.grabbedElement.lastTransformation.type == Transformations.REFLECTION) {
|
||||
this.grabbedElement.transformations.pop();
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.INVERSION);
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.INVERSION, undoable);
|
||||
} else if (!controlPressed && this.grabbedElement.lastTransformation.type == Transformations.INVERSION) {
|
||||
this.grabbedElement.transformations.pop();
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.REFLECTION);
|
||||
this.grabbedElement.startTransformation(x, y, Transformations.REFLECTION, undoable);
|
||||
}
|
||||
|
||||
this.grabbedElement.updateTransformation(x, y);
|
||||
|
|
@ -655,6 +656,11 @@ var DrawingArea = new Lang.Class({
|
|||
}
|
||||
|
||||
this.motionHandler = this.connect('motion-event', (actor, event) => {
|
||||
if (this.motionTimeout) {
|
||||
GLib.source_remove(this.motionTimeout);
|
||||
this.motionTimeout = null;
|
||||
}
|
||||
|
||||
if (this.spaceKeyPressed)
|
||||
return;
|
||||
|
||||
|
|
@ -664,6 +670,30 @@ var DrawingArea = new Lang.Class({
|
|||
return;
|
||||
let controlPressed = event.has_control_modifier();
|
||||
this._updateDrawing(x, y, controlPressed);
|
||||
|
||||
if (this.currentTool == Shapes.NONE) {
|
||||
let device = event.get_device();
|
||||
let sequence = event.get_event_sequence();
|
||||
|
||||
// Minimum time between two motion events is about 33 ms.
|
||||
// Add intermediate points to make quick free drawings smoother.
|
||||
this.motionTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, MOTION_TIME, () => {
|
||||
let [success, coords] = device.get_coords(sequence);
|
||||
if (!success)
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
|
||||
let [s, x, y] = this.transform_stage_point(coords.x, coords.y);
|
||||
if (!s)
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
|
||||
// Important: do not call this._updateDrawing because the area MUST NOT BE REDISPLAYED at this step.
|
||||
// It would lead to critical issues (bad performances and shell crashes).
|
||||
// The area will be redisplayed, including the intermediate points, at the next motion event.
|
||||
this.currentElement.addIntermediatePoint(x, y, controlPressed);
|
||||
|
||||
return GLib.SOURCE_CONTINUE;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -678,6 +708,10 @@ var DrawingArea = new Lang.Class({
|
|||
},
|
||||
|
||||
_stopDrawing: function() {
|
||||
if (this.motionTimeout) {
|
||||
GLib.source_remove(this.motionTimeout);
|
||||
this.motionTimeout = null;
|
||||
}
|
||||
if (this.motionHandler) {
|
||||
this.disconnect(this.motionHandler);
|
||||
this.motionHandler = null;
|
||||
|
|
@ -872,18 +906,36 @@ var DrawingArea = new Lang.Class({
|
|||
deleteLastElement: function() {
|
||||
this._stopAll();
|
||||
this.elements.pop();
|
||||
|
||||
if (this.elements.length)
|
||||
this.elements[this.elements.length - 1].resetUndoneTransformations();
|
||||
|
||||
this._redisplay();
|
||||
},
|
||||
|
||||
undo: function() {
|
||||
if (this.elements.length > 0)
|
||||
if (!this.elements.length)
|
||||
return;
|
||||
|
||||
let success = this.elements[this.elements.length - 1].undoTransformation();
|
||||
if (!success) {
|
||||
this.undoneElements.push(this.elements.pop());
|
||||
if (this.elements.length)
|
||||
this.elements[this.elements.length - 1].resetUndoneTransformations();
|
||||
}
|
||||
|
||||
this._redisplay();
|
||||
},
|
||||
|
||||
redo: function() {
|
||||
if (this.undoneElements.length > 0)
|
||||
let success = false;
|
||||
|
||||
if (this.elements.length)
|
||||
success = this.elements[this.elements.length - 1].redoTransformation();
|
||||
|
||||
if (!success && this.undoneElements.length > 0)
|
||||
this.elements.push(this.undoneElements.pop());
|
||||
|
||||
this._redisplay();
|
||||
},
|
||||
|
||||
|
|
@ -1130,6 +1182,21 @@ var DrawingArea = new Lang.Class({
|
|||
this.toggleHelp();
|
||||
if (this.textEntry && this.reactive)
|
||||
this.textEntry.grab_key_focus();
|
||||
|
||||
if (this.reactive) {
|
||||
this.stageKeyPressedHandler = global.stage.connect('key-press-event', this._onStageKeyPressed.bind(this));
|
||||
this.stageKeyReleasedHandler = global.stage.connect('key-release-event', this._onStageKeyReleased.bind(this));
|
||||
} else {
|
||||
if (this.stageKeyPressedHandler) {
|
||||
global.stage.disconnect(this.stageKeyPressedHandler);
|
||||
this.stageKeyPressedHandler = null;
|
||||
}
|
||||
if (this.stageKeyReleasedHandler) {
|
||||
global.stage.disconnect(this.stageKeyReleasedHandler);
|
||||
this.stageKeyReleasedHandler = null;
|
||||
}
|
||||
this.spaceKeyPressed = false;
|
||||
}
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
|
|
@ -1144,8 +1211,6 @@ var DrawingArea = new Lang.Class({
|
|||
},
|
||||
|
||||
enterDrawingMode: function() {
|
||||
this.stageKeyPressedHandler = global.stage.connect('key-press-event', this._onStageKeyPressed.bind(this));
|
||||
this.stageKeyReleasedHandler = global.stage.connect('key-release-event', this._onStageKeyReleased.bind(this));
|
||||
this.keyPressedHandler = this.connect('key-press-event', this._onKeyPressed.bind(this));
|
||||
this.buttonPressedHandler = this.connect('button-press-event', this._onButtonPressed.bind(this));
|
||||
this.keyboardPopupMenuHandler = this.connect('popup-menu', this._onKeyboardPopupMenu.bind(this));
|
||||
|
|
@ -1154,14 +1219,6 @@ var DrawingArea = new Lang.Class({
|
|||
},
|
||||
|
||||
leaveDrawingMode: function(save, erase) {
|
||||
if (this.stageKeyPressedHandler) {
|
||||
global.stage.disconnect(this.stageKeyPressedHandler);
|
||||
this.stageKeyPressedHandler = null;
|
||||
}
|
||||
if (this.stageKeyReleasedHandler) {
|
||||
global.stage.disconnect(this.stageKeyReleasedHandler);
|
||||
this.stageKeyReleasedHandler = null;
|
||||
}
|
||||
if (this.keyPressedHandler) {
|
||||
this.disconnect(this.keyPressedHandler);
|
||||
this.keyPressedHandler = null;
|
||||
|
|
|
|||
96
elements.js
96
elements.js
|
|
@ -28,9 +28,10 @@ const Pango = imports.gi.Pango;
|
|||
const PangoCairo = imports.gi.PangoCairo;
|
||||
|
||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||
const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_');
|
||||
|
||||
var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6, IMAGE: 7 };
|
||||
var Transformations = { TRANSLATION: 0, ROTATION: 1, SCALE_PRESERVE: 2, STRETCH: 3, REFLECTION: 4, INVERSION: 5 };
|
||||
var Transformations = { TRANSLATION: 0, ROTATION: 1, SCALE_PRESERVE: 2, STRETCH: 3, REFLECTION: 4, INVERSION: 5, SMOOTH: 100 };
|
||||
|
||||
var getAllFontFamilies = function() {
|
||||
return PangoCairo.font_map_get_default().list_families().map(fontFamily => fontFamily.get_name()).sort((a,b) => a.localeCompare(b));
|
||||
|
|
@ -61,6 +62,7 @@ const MIN_REFLECTION_LINE_LENGTH = 10; // px
|
|||
const MIN_TRANSLATION_DISTANCE = 1; // px
|
||||
const MIN_ROTATION_ANGLE = Math.PI / 1000; // rad
|
||||
const MIN_DRAWING_SIZE = 3; // px
|
||||
const MIN_INTERMEDIATE_POINT_DISTANCE = 1; // px, the higher it is, the fewer points there will be
|
||||
|
||||
var DrawingElement = function(params) {
|
||||
return params.shape == Shapes.TEXT ? new TextElement(params) :
|
||||
|
|
@ -72,7 +74,7 @@ var DrawingElement = function(params) {
|
|||
// It can be converted into a cairo path as well as a svg element.
|
||||
// See DrawingArea._startDrawing() to know its params.
|
||||
const _DrawingElement = new Lang.Class({
|
||||
Name: `${Me.uuid}.DrawingElement`,
|
||||
Name: `${UUID}-DrawingElement`,
|
||||
|
||||
_init: function(params) {
|
||||
for (let key in params)
|
||||
|
|
@ -121,7 +123,8 @@ const _DrawingElement = new Lang.Class({
|
|||
fill: this.fill,
|
||||
fillRule: this.fillRule,
|
||||
eraser: this.eraser,
|
||||
transformations: this.transformations,
|
||||
transformations: this.transformations.filter(transformation => transformation.type != Transformations.SMOOTH)
|
||||
.map(transformation => Object.assign({}, transformation, { undoable: undefined })),
|
||||
points: this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100])
|
||||
};
|
||||
},
|
||||
|
|
@ -400,9 +403,19 @@ const _DrawingElement = new Lang.Class({
|
|||
},
|
||||
|
||||
smoothAll: function() {
|
||||
for (let i = 0; i < this.points.length; i++) {
|
||||
let oldPoints = this.points.slice();
|
||||
|
||||
for (let i = 0; i < this.points.length; i++)
|
||||
this._smooth(i);
|
||||
}
|
||||
|
||||
let newPoints = this.points.slice();
|
||||
|
||||
this.transformations.push({ type: Transformations.SMOOTH, undoable: true,
|
||||
undo: () => this.points = oldPoints,
|
||||
redo: () => this.points = newPoints });
|
||||
|
||||
if (this._undoneTransformations)
|
||||
this._undoneTransformations = this._undoneTransformations.filter(transformation => transformation.type != Transformations.SMOOTH);
|
||||
},
|
||||
|
||||
addPoint: function() {
|
||||
|
|
@ -421,6 +434,17 @@ const _DrawingElement = new Lang.Class({
|
|||
}
|
||||
},
|
||||
|
||||
// For free drawing only.
|
||||
addIntermediatePoint: function(x, y, transform) {
|
||||
let points = this.points;
|
||||
if (getNearness(points[points.length - 1], [x, y], MIN_INTERMEDIATE_POINT_DISTANCE))
|
||||
return;
|
||||
|
||||
points.push([x, y]);
|
||||
if (transform)
|
||||
this._smooth(points.length - 1);
|
||||
},
|
||||
|
||||
startDrawing: function(startX, startY) {
|
||||
this.points.push([startX, startY]);
|
||||
|
||||
|
|
@ -480,18 +504,18 @@ const _DrawingElement = new Lang.Class({
|
|||
this.transformations.shift();
|
||||
},
|
||||
|
||||
startTransformation: function(startX, startY, type) {
|
||||
startTransformation: function(startX, startY, type, undoable) {
|
||||
if (type == Transformations.TRANSLATION)
|
||||
this.transformations.push({ startX: startX, startY: startY, type: type, slideX: 0, slideY: 0 });
|
||||
this.transformations.push({ startX, startY, type, undoable, slideX: 0, slideY: 0 });
|
||||
else if (type == Transformations.ROTATION)
|
||||
this.transformations.push({ startX: startX, startY: startY, type: type, angle: 0 });
|
||||
this.transformations.push({ startX, startY, type, undoable, angle: 0 });
|
||||
else if (type == Transformations.SCALE_PRESERVE || type == Transformations.STRETCH)
|
||||
this.transformations.push({ startX: startX, startY: startY, type: type, scaleX: 1, scaleY: 1, angle: 0 });
|
||||
this.transformations.push({ startX, startY, type, undoable, scaleX: 1, scaleY: 1, angle: 0 });
|
||||
else if (type == Transformations.REFLECTION)
|
||||
this.transformations.push({ startX: startX, startY: startY, endX: startX, endY: startY, type: type,
|
||||
this.transformations.push({ startX, startY, endX: startX, endY: startY, type, undoable,
|
||||
scaleX: 1, scaleY: 1, slideX: 0, slideY: 0, angle: 0 });
|
||||
else if (type == Transformations.INVERSION)
|
||||
this.transformations.push({ startX: startX, startY: startY, endX: startX, endY: startY, type: type,
|
||||
this.transformations.push({ startX, startY, endX: startX, endY: startY, type, undoable,
|
||||
scaleX: -1, scaleY: -1, slideX: startX, slideY: startY,
|
||||
angle: Math.PI + Math.atan(startY / (startX || 1)) });
|
||||
|
||||
|
|
@ -573,6 +597,52 @@ const _DrawingElement = new Lang.Class({
|
|||
}
|
||||
},
|
||||
|
||||
undoTransformation: function() {
|
||||
if (this.transformations && this.transformations.length) {
|
||||
// Do not undo initial transformations (transformations made during the drawing step).
|
||||
if (!this.lastTransformation.undoable)
|
||||
return false;
|
||||
|
||||
if (!this._undoneTransformations)
|
||||
this._undoneTransformations = [];
|
||||
|
||||
let transformation = this.transformations.pop();
|
||||
if (transformation.type == Transformations.SMOOTH)
|
||||
transformation.undo();
|
||||
|
||||
this._undoneTransformations.push(transformation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
redoTransformation: function() {
|
||||
if (this._undoneTransformations && this._undoneTransformations.length) {
|
||||
if (!this.transformations)
|
||||
this.transformations = [];
|
||||
|
||||
let transformation = this._undoneTransformations.pop();
|
||||
if (transformation.type == Transformations.SMOOTH)
|
||||
transformation.redo();
|
||||
|
||||
this.transformations.push(transformation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
resetUndoneTransformations: function() {
|
||||
delete this._undoneTransformations;
|
||||
},
|
||||
|
||||
get canUndo() {
|
||||
return this._undoneTransformations && this._undoneTransformations.length ? true : false;
|
||||
},
|
||||
|
||||
// The figure rotation center before transformations (original).
|
||||
// this.textWidth is computed during Cairo building.
|
||||
_getOriginalCenter: function() {
|
||||
|
|
@ -626,7 +696,7 @@ const _DrawingElement = new Lang.Class({
|
|||
});
|
||||
|
||||
const TextElement = new Lang.Class({
|
||||
Name: `${Me.uuid}.TextElement`,
|
||||
Name: `${UUID}-TextElement`,
|
||||
Extends: _DrawingElement,
|
||||
|
||||
toJSON: function() {
|
||||
|
|
@ -766,7 +836,7 @@ const TextElement = new Lang.Class({
|
|||
});
|
||||
|
||||
const ImageElement = new Lang.Class({
|
||||
Name: `${Me.uuid}.ImageElement`,
|
||||
Name: `${UUID}-ImageElement`,
|
||||
Extends: _DrawingElement,
|
||||
|
||||
toJSON: function() {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext;
|
|||
|
||||
const GS_VERSION = Config.PACKAGE_VERSION;
|
||||
const HIDE_TIMEOUT_LONG = 2500; // ms, default is 1500 ms
|
||||
const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_');
|
||||
|
||||
// custom Shell.ActionMode, assuming that they are unused
|
||||
const DRAWING_ACTION_MODE = Math.pow(2,14);
|
||||
|
|
@ -53,7 +54,7 @@ function init() {
|
|||
}
|
||||
|
||||
const Extension = new Lang.Class({
|
||||
Name: `${Me.uuid}.Extension`,
|
||||
Name: `${UUID}-Extension`,
|
||||
|
||||
_init: function() {
|
||||
Convenience.initTranslations();
|
||||
|
|
@ -81,7 +82,7 @@ const Extension = new Lang.Class({
|
|||
// distributes keybinding callbacks to the active area
|
||||
// and handles stylesheet and monitor changes.
|
||||
const AreaManager = new Lang.Class({
|
||||
Name: `${Me.uuid}.AreaManager`,
|
||||
Name: `${UUID}-AreaManager`,
|
||||
|
||||
_init: function() {
|
||||
this.areas = [];
|
||||
|
|
@ -560,7 +561,7 @@ const AreaManager = new Lang.Class({
|
|||
|
||||
// The same as the original, without forcing a ratio of 1.
|
||||
const OsdWindowConstraint = new Lang.Class({
|
||||
Name: `${Me.uuid}.OsdWindowConstraint`,
|
||||
Name: `${UUID}-OsdWindowConstraint`,
|
||||
Extends: OsdWindow.OsdWindowConstraint,
|
||||
|
||||
vfunc_update_allocation: function(actor, actorBox) {
|
||||
|
|
@ -582,7 +583,7 @@ const OsdWindowConstraint = new Lang.Class({
|
|||
});
|
||||
|
||||
const DrawingIndicator = new Lang.Class({
|
||||
Name: `${Me.uuid}.Indicator`,
|
||||
Name: `${UUID}-Indicator`,
|
||||
|
||||
_init: function() {
|
||||
let [menuAlignment, dontCreateMenu] = [0, true];
|
||||
|
|
|
|||
9
files.js
9
files.js
|
|
@ -31,6 +31,7 @@ const St = imports.gi.St;
|
|||
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Me = ExtensionUtils.getCurrentExtension();
|
||||
const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_');
|
||||
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();
|
||||
|
|
@ -98,7 +99,7 @@ const replaceColor = function (contents, color) {
|
|||
// 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: `${Me.uuid}.Image`,
|
||||
Name: `${UUID}-Image`,
|
||||
|
||||
_init: function(params) {
|
||||
for (let key in params)
|
||||
|
|
@ -193,7 +194,7 @@ var Image = new Lang.Class({
|
|||
|
||||
// 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: `${Me.uuid}.ImageWithGicon`,
|
||||
Name: `${UUID}-ImageWithGicon`,
|
||||
Extends: Image,
|
||||
|
||||
get displayName() {
|
||||
|
|
@ -247,7 +248,7 @@ const ImageWithGicon = new Lang.Class({
|
|||
|
||||
// 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: `${Me.uuid}.ImageFromJson`,
|
||||
Name: `${UUID}-ImageFromJson`,
|
||||
Extends: Image,
|
||||
contentType: 'image/svg+xml',
|
||||
|
||||
|
|
@ -394,7 +395,7 @@ var Images = {
|
|||
|
||||
// Wrapper around a json file (drawing saves).
|
||||
var Json = new Lang.Class({
|
||||
Name: `${Me.uuid}.Json`,
|
||||
Name: `${UUID}-Json`,
|
||||
|
||||
_init: function(params) {
|
||||
for (let key in params)
|
||||
|
|
|
|||
|
|
@ -40,11 +40,12 @@ const Tweener = GS_VERSION < '3.33.0' ? imports.ui.tweener : null;
|
|||
const HELPER_ANIMATION_TIME = 0.25;
|
||||
const MEDIA_KEYS_SCHEMA = 'org.gnome.settings-daemon.plugins.media-keys';
|
||||
const MEDIA_KEYS_KEYS = ['screenshot', 'screenshot-clip', 'area-screenshot', 'area-screenshot-clip'];
|
||||
const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_');
|
||||
|
||||
// DrawingHelper provides the "help osd" (Ctrl + F1)
|
||||
// It uses the same texts as in prefs
|
||||
var DrawingHelper = new Lang.Class({
|
||||
Name: `${Me.uuid}.DrawingHelper`,
|
||||
Name: `${UUID}-DrawingHelper`,
|
||||
Extends: St.ScrollView,
|
||||
|
||||
_init: function(params, monitor) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Draw On Your Screen\n"
|
||||
"Report-Msgid-Bugs-To: https://framagit.org/abakkk/DrawOnYourScreen/issues\n"
|
||||
"POT-Creation-Date: 2020-09-19 15:32+0200\n"
|
||||
"POT-Creation-Date: 2020-10-04 22:45+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"
|
||||
|
|
@ -265,12 +265,6 @@ msgctxt "drawing-tool"
|
|||
msgid "Mirror"
|
||||
msgstr ""
|
||||
|
||||
msgid "Undo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Redo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Erase"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -586,7 +580,7 @@ msgstr ""
|
|||
msgid "Add images from the clipboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Redo last brushstroke"
|
||||
msgid "Redo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Save drawing"
|
||||
|
|
@ -710,5 +704,5 @@ msgstr ""
|
|||
msgid "Square drawing area"
|
||||
msgstr ""
|
||||
|
||||
msgid "Undo last brushstroke"
|
||||
msgid "Undo"
|
||||
msgstr ""
|
||||
|
|
|
|||
14
menu.js
14
menu.js
|
|
@ -46,6 +46,7 @@ const GS_VERSION = Config.PACKAGE_VERSION;
|
|||
const FONT_FAMILY_STYLE = true;
|
||||
// use 'login-dialog-message-warning' class in order to get GS theme warning color (default: #f57900)
|
||||
const WARNING_COLOR_STYLE_CLASS_NAME = 'login-dialog-message-warning';
|
||||
const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_');
|
||||
|
||||
const getActor = function(object) {
|
||||
return GS_VERSION < '3.33.0' ? object.actor : object;
|
||||
|
|
@ -139,7 +140,7 @@ var DisplayStrings = {
|
|||
};
|
||||
|
||||
var DrawingMenu = new Lang.Class({
|
||||
Name: `${Me.uuid}.DrawingMenu`,
|
||||
Name: `${UUID}-DrawingMenu`,
|
||||
|
||||
_init: function(area, monitor, drawingTools) {
|
||||
this.area = area;
|
||||
|
|
@ -229,12 +230,11 @@ var DrawingMenu = new Lang.Class({
|
|||
this.menu.removeAll();
|
||||
|
||||
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.undoButton = new ActionButton(getSummary('undo'), 'edit-undo-symbolic', this.area.undo.bind(this.area), this._updateActionSensitivity.bind(this));
|
||||
this.redoButton = new ActionButton(getSummary('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);
|
||||
|
|
@ -308,7 +308,7 @@ var DrawingMenu = new Lang.Class({
|
|||
|
||||
_updateActionSensitivity: function() {
|
||||
this.undoButton.child.reactive = this.area.elements.length > 0;
|
||||
this.redoButton.child.reactive = this.area.undoneElements.length > 0;
|
||||
this.redoButton.child.reactive = this.area.undoneElements.length > 0 || (this.area.elements.length && this.area.elements[this.area.elements.length - 1].canUndo);
|
||||
this.eraseButton.child.reactive = this.area.elements.length > 0;
|
||||
this.smoothButton.child.reactive = this.area.elements.length > 0 && this.area.elements[this.area.elements.length - 1].shape == this.drawingTools.NONE;
|
||||
this.saveButton.child.reactive = this.area.elements.length > 0;
|
||||
|
|
@ -742,7 +742,7 @@ const updateSubMenuAdjustment = function(itemActor) {
|
|||
|
||||
// An action button that uses upstream dash item tooltips.
|
||||
const ActionButton = new Lang.Class({
|
||||
Name: `${Me.uuid}.DrawingMenuActionButton`,
|
||||
Name: `${UUID}-DrawingMenuActionButton`,
|
||||
Extends: St.Bin,
|
||||
_labelShowing: false,
|
||||
_resetHoverTimeoutId: 0,
|
||||
|
|
@ -787,7 +787,7 @@ const ActionButton = new Lang.Class({
|
|||
|
||||
// based on searchItem.js, https://github.com/leonardo-bartoli/gnome-shell-extension-Recents
|
||||
const Entry = new Lang.Class({
|
||||
Name: `${Me.uuid}.DrawingMenuEntry`,
|
||||
Name: `${UUID}-DrawingMenuEntry`,
|
||||
|
||||
_init: function(params) {
|
||||
this.params = params;
|
||||
|
|
|
|||
|
|
@ -18,5 +18,5 @@
|
|||
"3.36",
|
||||
"3.38"
|
||||
],
|
||||
"version": 8
|
||||
"version": 8.1
|
||||
}
|
||||
|
|
|
|||
36
prefs.js
36
prefs.js
|
|
@ -43,11 +43,7 @@ const _GTK = imports.gettext.domain('gtk30').gettext;
|
|||
|
||||
const MARGIN = 10;
|
||||
const ROWBOX_MARGIN_PARAMS = { margin_top: MARGIN / 2, margin_bottom: MARGIN / 2, margin_left: MARGIN, margin_right: MARGIN };
|
||||
|
||||
// GTypeName is not sanitized in GS 3.28-
|
||||
const sanitizeGType = function(name) {
|
||||
return `Gjs_${name.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_')}`;
|
||||
}
|
||||
const UUID = Me.uuid.replace(/@/gi, '_at_').replace(/[^a-z0-9+_-]/gi, '_');
|
||||
|
||||
function init() {
|
||||
Convenience.initTranslations();
|
||||
|
|
@ -68,8 +64,7 @@ function buildPrefsWidget() {
|
|||
}
|
||||
|
||||
const TopStack = new GObject.Class({
|
||||
Name: `${Me.uuid}.TopStack`,
|
||||
GTypeName: sanitizeGType(`${Me.uuid}-TopStack`),
|
||||
Name: `${UUID}-TopStack`,
|
||||
Extends: Gtk.Stack,
|
||||
|
||||
_init: function(params) {
|
||||
|
|
@ -87,8 +82,7 @@ const TopStack = new GObject.Class({
|
|||
});
|
||||
|
||||
const AboutPage = new GObject.Class({
|
||||
Name: `${Me.uuid}.AboutPage`,
|
||||
GTypeName: sanitizeGType(`${Me.uuid}-AboutPage`),
|
||||
Name: `${UUID}-AboutPage`,
|
||||
Extends: Gtk.ScrolledWindow,
|
||||
|
||||
_init: function(params) {
|
||||
|
|
@ -141,8 +135,7 @@ const AboutPage = new GObject.Class({
|
|||
});
|
||||
|
||||
const DrawingPage = new GObject.Class({
|
||||
Name: `${Me.uuid}.DrawingPage`,
|
||||
GTypeName: sanitizeGType(`${Me.uuid}-DrawingPage`),
|
||||
Name: `${UUID}-DrawingPage`,
|
||||
Extends: Gtk.ScrolledWindow,
|
||||
|
||||
_init: function(params) {
|
||||
|
|
@ -394,8 +387,7 @@ const DrawingPage = new GObject.Class({
|
|||
});
|
||||
|
||||
const PrefsPage = new GObject.Class({
|
||||
Name: `${Me.uuid}.PrefsPage`,
|
||||
GTypeName: sanitizeGType(`${Me.uuid}-PrefsPage`),
|
||||
Name: `${UUID}-PrefsPage`,
|
||||
Extends: Gtk.ScrolledWindow,
|
||||
|
||||
_init: function(params) {
|
||||
|
|
@ -508,8 +500,7 @@ const PrefsPage = new GObject.Class({
|
|||
});
|
||||
|
||||
const Frame = new GObject.Class({
|
||||
Name: `${Me.uuid}.Frame`,
|
||||
GTypeName: sanitizeGType(`${Me.uuid}-Frame`),
|
||||
Name: `${UUID}-Frame`,
|
||||
Extends: Gtk.Frame,
|
||||
|
||||
_init: function(params) {
|
||||
|
|
@ -524,8 +515,7 @@ const Frame = new GObject.Class({
|
|||
});
|
||||
|
||||
const PrefRow = new GObject.Class({
|
||||
Name: `${Me.uuid}.PrefRow`,
|
||||
GTypeName: sanitizeGType(`${Me.uuid}-PrefRow`),
|
||||
Name: `${UUID}-PrefRow`,
|
||||
Extends: Gtk.ListBoxRow,
|
||||
|
||||
_init: function(params) {
|
||||
|
|
@ -569,8 +559,7 @@ const PrefRow = new GObject.Class({
|
|||
});
|
||||
|
||||
const PixelSpinButton = new GObject.Class({
|
||||
Name: `${Me.uuid}.PixelSpinButton`,
|
||||
GTypeName: sanitizeGType(`${Me.uuid}-PixelSpinButton`),
|
||||
Name: `${UUID}-PixelSpinButton`,
|
||||
Extends: Gtk.SpinButton,
|
||||
Properties: {
|
||||
'range': GObject.param_spec_variant('range', 'range', 'GSettings range',
|
||||
|
|
@ -609,8 +598,7 @@ const PixelSpinButton = new GObject.Class({
|
|||
|
||||
// A color button that can be easily bound with a color string setting.
|
||||
const ColorStringButton = new GObject.Class({
|
||||
Name: `${Me.uuid}.ColorStringButton`,
|
||||
GTypeName: sanitizeGType(`${Me.uuid}-ColorStringButton`),
|
||||
Name: `${UUID}-ColorStringButton`,
|
||||
Extends: Gtk.ColorButton,
|
||||
Properties: {
|
||||
'color-string': GObject.ParamSpec.string('color-string', 'colorString', 'A string that describes the color',
|
||||
|
|
@ -642,8 +630,7 @@ const ColorStringButton = new GObject.Class({
|
|||
});
|
||||
|
||||
const FileChooserButton = new GObject.Class({
|
||||
Name: `${Me.uuid}.FileChooserButton`,
|
||||
GTypeName: sanitizeGType(`${Me.uuid}-FileChooserButton`),
|
||||
Name: `${UUID}-FileChooserButton`,
|
||||
Extends: Gtk.FileChooserButton,
|
||||
Properties: {
|
||||
'location': GObject.ParamSpec.string('location', 'location', 'location',
|
||||
|
|
@ -674,8 +661,7 @@ const FileChooserButton = new GObject.Class({
|
|||
|
||||
// this code comes from Sticky Notes View by Sam Bull, https://extensions.gnome.org/extension/568/notes/
|
||||
const KeybindingsWidget = new GObject.Class({
|
||||
Name: `${Me.uuid}.KeybindingsWidget`,
|
||||
GTypeName: sanitizeGType(`${Me.uuid}-KeybindingsWidget`),
|
||||
Name: `${UUID}-KeybindingsWidget`,
|
||||
Extends: Gtk.Box,
|
||||
|
||||
_init: function(settingKeys, settings) {
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@
|
|||
</key>
|
||||
<key type="as" name="redo">
|
||||
<default>["<Primary><Shift>z"]</default>
|
||||
<summary>Redo last brushstroke</summary>
|
||||
<summary>Redo</summary>
|
||||
</key>
|
||||
<key type="as" name="save-as-json">
|
||||
<default>["<Primary>s"]</default>
|
||||
|
|
@ -340,7 +340,7 @@
|
|||
</key>
|
||||
<key type="as" name="undo">
|
||||
<default>["<Primary>z"]</default>
|
||||
<summary>Undo last brushstroke</summary>
|
||||
<summary>Undo</summary>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
|
|
|
|||
Loading…
Reference in New Issue