Merge branch 'dev' into 'master'
v7 See merge request abakkk/DrawOnYourScreen!16
This commit is contained in:
commit
1db60b6d4e
7
NEWS
7
NEWS
|
|
@ -1,6 +1,13 @@
|
||||||
v6.4 - September 2020
|
v6.4 - September 2020
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|
* Can import GIMP palettes.
|
||||||
|
* Can color SVG images.
|
||||||
|
* Integrate color picker (GS buit-in or Color Picker extension).
|
||||||
|
|
||||||
|
v6.4 - September 2020
|
||||||
|
=====================
|
||||||
|
|
||||||
* Prepare GS 3.38 compatibility.
|
* Prepare GS 3.38 compatibility.
|
||||||
* Add tooltips to sub-menu buttons also.
|
* Add tooltips to sub-menu buttons also.
|
||||||
* Fix drawing directory monitoring when it does not exist yet.
|
* Fix drawing directory monitoring when it does not exist yet.
|
||||||
|
|
|
||||||
|
|
@ -52,3 +52,7 @@ Then save your beautiful work by taking a screenshot.
|
||||||
|
|
||||||
[Screenshot Tool](https://extensions.gnome.org/extension/1112/screenshot-tool/) is a convenient extension to “create, copy, store and upload screenshots”. In order to select a screenshoot area with your pointer while keeping the drawing in place, you need first to tell DrawOnYourScreen to ungrab the pointer (`Ctrl + Super + Alt + D`).
|
[Screenshot Tool](https://extensions.gnome.org/extension/1112/screenshot-tool/) is a convenient extension to “create, copy, store and upload screenshots”. In order to select a screenshoot area with your pointer while keeping the drawing in place, you need first to tell DrawOnYourScreen to ungrab the pointer (`Ctrl + Super + Alt + D`).
|
||||||
|
|
||||||
|
* Color Picker extension:
|
||||||
|
|
||||||
|
If the GNOME Shell built-in color picker is too basic for you, have a look at the [Color Picker extension](https://extensions.gnome.org/extension/3396/color-picker), which let's you select the pixel accurately, preview the color and adjust its values. Once installed and enabled, it will be transparently integrated into DrawOnYourScreen.
|
||||||
|
|
||||||
|
|
|
||||||
106
area.js
106
area.js
|
|
@ -29,6 +29,7 @@ const GObject = imports.gi.GObject;
|
||||||
const Gtk = imports.gi.Gtk;
|
const Gtk = imports.gi.Gtk;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
const Pango = imports.gi.Pango;
|
const Pango = imports.gi.Pango;
|
||||||
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const System = imports.system;
|
const System = imports.system;
|
||||||
|
|
||||||
|
|
@ -49,6 +50,7 @@ const SVG_DEBUG_EXTENDS = false;
|
||||||
const TEXT_CURSOR_TIME = 600; // ms
|
const TEXT_CURSOR_TIME = 600; // ms
|
||||||
const ELEMENT_GRABBER_TIME = 80; // ms, default is about 16 ms
|
const ELEMENT_GRABBER_TIME = 80; // ms, default is about 16 ms
|
||||||
const GRID_TILES_HORIZONTAL_NUMBER = 30;
|
const GRID_TILES_HORIZONTAL_NUMBER = 30;
|
||||||
|
const COLOR_PICKER_EXTENSION_UUID = 'color-picker@tuberry';
|
||||||
|
|
||||||
const { Shapes, Transformations } = Elements;
|
const { Shapes, Transformations } = Elements;
|
||||||
const { DisplayStrings } = Menu;
|
const { DisplayStrings } = Menu;
|
||||||
|
|
@ -62,15 +64,20 @@ var Tools = Object.assign({
|
||||||
}, Shapes, Manipulations);
|
}, Shapes, Manipulations);
|
||||||
Object.defineProperty(Tools, 'getNameOf', { enumerable: false });
|
Object.defineProperty(Tools, 'getNameOf', { enumerable: false });
|
||||||
|
|
||||||
const getClutterColorFromString = function(string, fallback) {
|
// toJSON provides a string suitable for SVG color attribute whereas
|
||||||
let [success, color] = Clutter.Color.from_string(string);
|
// toString provides a string suitable for displaying the color name to the user.
|
||||||
color.toString = () => string;
|
const getColorFromString = function(string, fallback) {
|
||||||
|
let [colorString, displayName] = string.split(':');
|
||||||
|
let [success, color] = Clutter.Color.from_string(colorString);
|
||||||
|
color.toJSON = () => colorString;
|
||||||
|
color.toString = () => displayName || colorString;
|
||||||
if (success)
|
if (success)
|
||||||
return color;
|
return color;
|
||||||
|
|
||||||
log(`${Me.metadata.uuid}: "${string}" color cannot be parsed.`);
|
log(`${Me.metadata.uuid}: "${string}" color cannot be parsed.`);
|
||||||
color = Clutter.Color.get_static(Clutter.StaticColor[fallback.toUpperCase()]);
|
color = Clutter.Color.get_static(Clutter.StaticColor[fallback.toUpperCase()]);
|
||||||
color.toString = () => fallback.slice(0, 1).toUpperCase() + fallback.slice(1);
|
color.toJSON = () => fallback;
|
||||||
|
color.toString = () => fallback;
|
||||||
return color;
|
return color;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -152,7 +159,7 @@ var DrawingArea = new Lang.Class({
|
||||||
|
|
||||||
set currentPalette(palette) {
|
set currentPalette(palette) {
|
||||||
this._currentPalette = palette;
|
this._currentPalette = palette;
|
||||||
this.colors = palette[1].map(colorString => getClutterColorFromString(colorString, 'white'));
|
this.colors = palette[1].map(colorString => getColorFromString(colorString, 'White'));
|
||||||
if (!this.colors[0])
|
if (!this.colors[0])
|
||||||
this.colors.push(Clutter.Color.get_static(Clutter.StaticColor.WHITE));
|
this.colors.push(Clutter.Color.get_static(Clutter.StaticColor.WHITE));
|
||||||
},
|
},
|
||||||
|
|
@ -254,9 +261,9 @@ var DrawingArea = new Lang.Class({
|
||||||
this.squareAreaSize = Me.drawingSettings.get_uint('square-area-size');
|
this.squareAreaSize = Me.drawingSettings.get_uint('square-area-size');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.areaBackgroundColor = getClutterColorFromString(Me.drawingSettings.get_string('background-color'), 'black');
|
this.areaBackgroundColor = getColorFromString(Me.drawingSettings.get_string('background-color'), 'Black');
|
||||||
|
|
||||||
this.gridColor = getClutterColorFromString(Me.drawingSettings.get_string('grid-color'), 'gray');
|
this.gridColor = getColorFromString(Me.drawingSettings.get_string('grid-color'), 'Gray');
|
||||||
if (Me.drawingSettings.get_boolean('grid-line-auto')) {
|
if (Me.drawingSettings.get_boolean('grid-line-auto')) {
|
||||||
this.gridLineSpacing = Math.round(this.monitor.width / (5 * GRID_TILES_HORIZONTAL_NUMBER));
|
this.gridLineSpacing = Math.round(this.monitor.width / (5 * GRID_TILES_HORIZONTAL_NUMBER));
|
||||||
this.gridLineWidth = this.gridLineSpacing / 20;
|
this.gridLineWidth = this.gridLineSpacing / 20;
|
||||||
|
|
@ -596,7 +603,7 @@ var DrawingArea = new Lang.Class({
|
||||||
this._redisplay();
|
this._redisplay();
|
||||||
},
|
},
|
||||||
|
|
||||||
_startDrawing: function(stageX, stageY, eraser) {
|
_startDrawing: function(stageX, stageY, shiftPressed) {
|
||||||
let [success, startX, startY] = this.transform_stage_point(stageX, stageY);
|
let [success, startX, startY] = this.transform_stage_point(stageX, stageY);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
|
|
@ -610,7 +617,7 @@ var DrawingArea = new Lang.Class({
|
||||||
this.currentElement = new Elements.DrawingElement({
|
this.currentElement = new Elements.DrawingElement({
|
||||||
shape: this.currentTool,
|
shape: this.currentTool,
|
||||||
color: this.currentColor,
|
color: this.currentColor,
|
||||||
eraser: eraser,
|
eraser: shiftPressed,
|
||||||
font: this.currentFont.copy(),
|
font: this.currentFont.copy(),
|
||||||
// Translators: initial content of the text area
|
// Translators: initial content of the text area
|
||||||
text: pgettext("text-area-content", "Text"),
|
text: pgettext("text-area-content", "Text"),
|
||||||
|
|
@ -621,16 +628,15 @@ var DrawingArea = new Lang.Class({
|
||||||
this.currentElement = new Elements.DrawingElement({
|
this.currentElement = new Elements.DrawingElement({
|
||||||
shape: this.currentTool,
|
shape: this.currentTool,
|
||||||
color: this.currentColor,
|
color: this.currentColor,
|
||||||
eraser: eraser,
|
colored: shiftPressed,
|
||||||
image: this.currentImage,
|
image: this.currentImage,
|
||||||
operator: this.currentOperator,
|
|
||||||
points: []
|
points: []
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.currentElement = new Elements.DrawingElement({
|
this.currentElement = new Elements.DrawingElement({
|
||||||
shape: this.currentTool,
|
shape: this.currentTool,
|
||||||
color: this.currentColor,
|
color: this.currentColor,
|
||||||
eraser: eraser,
|
eraser: shiftPressed,
|
||||||
fill: this.fill,
|
fill: this.fill,
|
||||||
fillRule: this.currentFillRule,
|
fillRule: this.currentFillRule,
|
||||||
line: { lineWidth: this.currentLineWidth, lineJoin: this.currentLineJoin, lineCap: this.currentLineCap },
|
line: { lineWidth: this.currentLineWidth, lineJoin: this.currentLineJoin, lineCap: this.currentLineCap },
|
||||||
|
|
@ -1032,6 +1038,78 @@ var DrawingArea = new Lang.Class({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onColorPicked: function(color) {
|
||||||
|
if (color instanceof Clutter.Color)
|
||||||
|
color = color.to_string().slice(0, -2);
|
||||||
|
|
||||||
|
this.currentColor = getColorFromString(color);
|
||||||
|
if (this.currentElement) {
|
||||||
|
this.currentElement.color = this.currentColor;
|
||||||
|
this._redisplay();
|
||||||
|
}
|
||||||
|
this.emit('show-osd', Files.Icons.COLOR, String(this.currentColor), this.currentColor.to_string().slice(0, 7), -1, false);
|
||||||
|
this.initPointerCursor();
|
||||||
|
},
|
||||||
|
|
||||||
|
pickColor: function() {
|
||||||
|
if (!Screenshot.PickPixel)
|
||||||
|
// GS 3.28-
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Translators: It is displayed in an OSD notification to ask the user to start picking, so it should use the imperative mood.
|
||||||
|
this.emit('show-osd', Files.Icons.COLOR_PICKER, pgettext("osd-notification", "Pick a color"), "", -1, false);
|
||||||
|
|
||||||
|
let extension = Main.extensionManager && Main.extensionManager.lookup(COLOR_PICKER_EXTENSION_UUID);
|
||||||
|
if (extension && extension.state == ExtensionUtils.ExtensionState.ENABLED && extension.stateObj && extension.stateObj.pickAsync) {
|
||||||
|
extension.stateObj.pickAsync().then(result => {
|
||||||
|
if (typeof result == 'string')
|
||||||
|
this._onColorPicked(result);
|
||||||
|
else
|
||||||
|
this.initPointerCursor();
|
||||||
|
}).catch(e => {
|
||||||
|
this.initPointerCursor();
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let screenshot = new Shell.Screenshot();
|
||||||
|
let pickPixel = new Screenshot.PickPixel(screenshot);
|
||||||
|
|
||||||
|
if (pickPixel.pickAsync) {
|
||||||
|
pickPixel.pickAsync().then(result => {
|
||||||
|
if (result instanceof Clutter.Color) {
|
||||||
|
// GS 3.38+
|
||||||
|
this._onColorPicked(result);
|
||||||
|
} else {
|
||||||
|
// GS 3.36
|
||||||
|
let graphenePoint = result;
|
||||||
|
screenshot.pick_color(graphenePoint.x, graphenePoint.y, (o, res) => {
|
||||||
|
let [, color] = screenshot.pick_color_finish(res);
|
||||||
|
this._onColorPicked(color);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(() => this.initPointerCursor());
|
||||||
|
} else {
|
||||||
|
// GS 3.34-
|
||||||
|
pickPixel.show();
|
||||||
|
pickPixel.connect('finished', (pickPixel, coords) => {
|
||||||
|
if (coords)
|
||||||
|
screenshot.pick_color(...coords, (o, res) => {
|
||||||
|
let [, color] = screenshot.pick_color_finish(res);
|
||||||
|
this._onColorPicked(color);
|
||||||
|
});
|
||||||
|
else
|
||||||
|
this.initPointerCursor();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
log(`${Me.metadata.uuid}: color picker failed: ${e.message}`);
|
||||||
|
this.initPointerCursor();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
toggleHelp: function() {
|
toggleHelp: function() {
|
||||||
if (this.helper.visible) {
|
if (this.helper.visible) {
|
||||||
this.helper.hideHelp();
|
this.helper.hideHelp();
|
||||||
|
|
@ -1120,7 +1198,7 @@ var DrawingArea = new Lang.Class({
|
||||||
|
|
||||||
elements.push(...JSON.parse(json.contents).map(object => {
|
elements.push(...JSON.parse(json.contents).map(object => {
|
||||||
if (object.color)
|
if (object.color)
|
||||||
object.color = getClutterColorFromString(object.color, 'white');
|
object.color = getColorFromString(object.color, 'White');
|
||||||
if (object.font && typeof object.font == 'string')
|
if (object.font && typeof object.font == 'string')
|
||||||
object.font = Pango.FontDescription.from_string(object.font);
|
object.font = Pango.FontDescription.from_string(object.font);
|
||||||
if (object.image)
|
if (object.image)
|
||||||
|
|
@ -1227,7 +1305,7 @@ var DrawingArea = new Lang.Class({
|
||||||
|
|
||||||
this.elements.push(...JSON.parse(json.contents).map(object => {
|
this.elements.push(...JSON.parse(json.contents).map(object => {
|
||||||
if (object.color)
|
if (object.color)
|
||||||
object.color = getClutterColorFromString(object.color, 'white');
|
object.color = getColorFromString(object.color, 'White');
|
||||||
if (object.font && typeof object.font == 'string')
|
if (object.font && typeof object.font == 'string')
|
||||||
object.font = Pango.FontDescription.from_string(object.font);
|
object.font = Pango.FontDescription.from_string(object.font);
|
||||||
if (object.image)
|
if (object.image)
|
||||||
|
|
|
||||||
27
elements.js
27
elements.js
|
|
@ -113,7 +113,7 @@ const _DrawingElement = new Lang.Class({
|
||||||
toJSON: function() {
|
toJSON: function() {
|
||||||
return {
|
return {
|
||||||
shape: this.shape,
|
shape: this.shape,
|
||||||
color: this.color.toString(),
|
color: this.color,
|
||||||
line: this.line,
|
line: this.line,
|
||||||
dash: this.dash,
|
dash: this.dash,
|
||||||
fill: this.fill,
|
fill: this.fill,
|
||||||
|
|
@ -314,7 +314,7 @@ const _DrawingElement = new Lang.Class({
|
||||||
_drawSvg: function(transAttribute, bgcolorString) {
|
_drawSvg: function(transAttribute, bgcolorString) {
|
||||||
let row = "\n ";
|
let row = "\n ";
|
||||||
let points = this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100]);
|
let points = this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100]);
|
||||||
let color = this.eraser ? bgcolorString : this.color.toString();
|
let color = this.eraser ? bgcolorString : this.color.toJSON();
|
||||||
let fill = this.fill && !this.isStraightLine;
|
let fill = this.fill && !this.isStraightLine;
|
||||||
let attributes = this.eraser ? `class="eraser" ` : '';
|
let attributes = this.eraser ? `class="eraser" ` : '';
|
||||||
|
|
||||||
|
|
@ -633,7 +633,7 @@ const TextElement = new Lang.Class({
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shape: this.shape,
|
shape: this.shape,
|
||||||
color: this.color.toString(),
|
color: this.color,
|
||||||
eraser: this.eraser,
|
eraser: this.eraser,
|
||||||
transformations: this.transformations,
|
transformations: this.transformations,
|
||||||
text: this.text,
|
text: this.text,
|
||||||
|
|
@ -703,7 +703,7 @@ const TextElement = new Lang.Class({
|
||||||
_drawSvg: function(transAttribute, bgcolorString) {
|
_drawSvg: function(transAttribute, bgcolorString) {
|
||||||
let row = "\n ";
|
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 [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 ? bgcolorString : this.color.toString();
|
let color = this.eraser ? bgcolorString : this.color.toJSON();
|
||||||
let attributes = this.eraser ? `class="eraser" ` : '';
|
let attributes = this.eraser ? `class="eraser" ` : '';
|
||||||
|
|
||||||
if (this.points.length == 2) {
|
if (this.points.length == 2) {
|
||||||
|
|
@ -767,19 +767,13 @@ const ImageElement = new Lang.Class({
|
||||||
Name: 'DrawOnYourScreenImageElement',
|
Name: 'DrawOnYourScreenImageElement',
|
||||||
Extends: _DrawingElement,
|
Extends: _DrawingElement,
|
||||||
|
|
||||||
_init: function(params) {
|
|
||||||
params.fill = false;
|
|
||||||
this.parent(params);
|
|
||||||
},
|
|
||||||
|
|
||||||
toJSON: function() {
|
toJSON: function() {
|
||||||
return {
|
return {
|
||||||
shape: this.shape,
|
shape: this.shape,
|
||||||
color: this.color.toString(),
|
color: this.color,
|
||||||
fill: this.fill,
|
colored: this.colored,
|
||||||
eraser: this.eraser,
|
|
||||||
transformations: this.transformations,
|
transformations: this.transformations,
|
||||||
image: this.image.toJson(),
|
image: this.image,
|
||||||
preserveAspectRatio: this.preserveAspectRatio,
|
preserveAspectRatio: this.preserveAspectRatio,
|
||||||
points: this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100])
|
points: this.points.map((point) => [Math.round(point[0]*100)/100, Math.round(point[1]*100)/100])
|
||||||
};
|
};
|
||||||
|
|
@ -797,7 +791,7 @@ const ImageElement = new Lang.Class({
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cr.save();
|
cr.save();
|
||||||
this.image.setCairoSource(cr, x, y, width, height, this.preserveAspectRatio);
|
this.image.setCairoSource(cr, x, y, width, height, this.preserveAspectRatio, this.colored ? this.color.toJSON() : null);
|
||||||
cr.rectangle(x, y, width, height);
|
cr.rectangle(x, y, width, height);
|
||||||
cr.fill();
|
cr.fill();
|
||||||
cr.restore();
|
cr.restore();
|
||||||
|
|
@ -819,14 +813,15 @@ const ImageElement = new Lang.Class({
|
||||||
_drawSvg: function(transAttribute) {
|
_drawSvg: function(transAttribute) {
|
||||||
let points = this.points;
|
let points = this.points;
|
||||||
let row = "\n ";
|
let row = "\n ";
|
||||||
let attributes = this.eraser ? `class="eraser" ` : '';
|
let attributes = '';
|
||||||
|
|
||||||
if (points.length == 2) {
|
if (points.length == 2) {
|
||||||
attributes += `fill="none"`;
|
attributes += `fill="none"`;
|
||||||
|
let base64 = this.image.getBase64ForColor(this.colored ? this.color.toJSON() : null);
|
||||||
row += `<image ${attributes} x="${Math.min(points[0][0], points[1][0])}" y="${Math.min(points[0][1], points[1][1])}" ` +
|
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} ` +
|
`width="${Math.abs(points[1][0] - points[0][0])}" height="${Math.abs(points[1][1] - points[0][1])}"${transAttribute} ` +
|
||||||
`preserveAspectRatio="${this.preserveAspectRatio ? 'xMinYMin' : 'none'}" ` +
|
`preserveAspectRatio="${this.preserveAspectRatio ? 'xMinYMin' : 'none'}" ` +
|
||||||
`id="${this.image.displayName}" xlink:href="data:${this.image.contentType};base64,${this.image.base64}"/>`;
|
`id="${this.image.displayName}" xlink:href="data:${this.image.contentType};base64,${base64}"/>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return row;
|
return row;
|
||||||
|
|
|
||||||
|
|
@ -225,6 +225,7 @@ const AreaManager = new Lang.Class({
|
||||||
'save-as-json': this.activeArea.saveAsJson.bind(this.activeArea, true, null),
|
'save-as-json': this.activeArea.saveAsJson.bind(this.activeArea, true, null),
|
||||||
'open-previous-json': this.activeArea.loadPreviousJson.bind(this.activeArea),
|
'open-previous-json': this.activeArea.loadPreviousJson.bind(this.activeArea),
|
||||||
'open-next-json': this.activeArea.loadNextJson.bind(this.activeArea),
|
'open-next-json': this.activeArea.loadNextJson.bind(this.activeArea),
|
||||||
|
'pick-color': this.activeArea.pickColor.bind(this.activeArea),
|
||||||
'toggle-background': this.activeArea.toggleBackground.bind(this.activeArea),
|
'toggle-background': this.activeArea.toggleBackground.bind(this.activeArea),
|
||||||
'toggle-grid': this.activeArea.toggleGrid.bind(this.activeArea),
|
'toggle-grid': this.activeArea.toggleGrid.bind(this.activeArea),
|
||||||
'toggle-square-area': this.activeArea.toggleSquareArea.bind(this.activeArea),
|
'toggle-square-area': this.activeArea.toggleSquareArea.bind(this.activeArea),
|
||||||
|
|
|
||||||
63
files.js
63
files.js
|
|
@ -41,6 +41,7 @@ const ICON_NAMES = [
|
||||||
'tool-ellipse', 'tool-line', 'tool-mirror', 'tool-move', 'tool-none', 'tool-polygon', 'tool-polyline', 'tool-rectangle', 'tool-resize',
|
'tool-ellipse', 'tool-line', 'tool-mirror', 'tool-move', 'tool-none', 'tool-polygon', 'tool-polyline', 'tool-rectangle', 'tool-resize',
|
||||||
];
|
];
|
||||||
const ThemedIconNames = {
|
const ThemedIconNames = {
|
||||||
|
COLOR_PICKER: 'color-select-symbolic',
|
||||||
ENTER: 'applications-graphics', LEAVE: 'application-exit',
|
ENTER: 'applications-graphics', LEAVE: 'application-exit',
|
||||||
GRAB: 'input-touchpad', UNGRAB: 'touchpad-disabled',
|
GRAB: 'input-touchpad', UNGRAB: 'touchpad-disabled',
|
||||||
OPEN: 'document-open', SAVE: 'document-save',
|
OPEN: 'document-open', SAVE: 'document-save',
|
||||||
|
|
@ -49,7 +50,16 @@ const ThemedIconNames = {
|
||||||
TOOL_IMAGE: 'insert-image', TOOL_TEXT: 'insert-text',
|
TOOL_IMAGE: 'insert-image', TOOL_TEXT: 'insert-text',
|
||||||
};
|
};
|
||||||
|
|
||||||
var Icons = {};
|
var Icons = {
|
||||||
|
get FAKE() {
|
||||||
|
if (!this._fake) {
|
||||||
|
let bytes = new GLib.Bytes('<svg/>');
|
||||||
|
this._fake = Gio.BytesIcon.new(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._fake;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ICON_NAMES.forEach(name => {
|
ICON_NAMES.forEach(name => {
|
||||||
Object.defineProperty(Icons, name.toUpperCase().replace(/-/gi, '_'), {
|
Object.defineProperty(Icons, name.toUpperCase().replace(/-/gi, '_'), {
|
||||||
|
|
@ -73,6 +83,18 @@ Object.keys(ThemedIconNames).forEach(key => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const replaceColor = function (contents, color) {
|
||||||
|
if (contents instanceof Uint8Array)
|
||||||
|
contents = ByteArray.toString(contents);
|
||||||
|
else
|
||||||
|
contents = contents.toString();
|
||||||
|
|
||||||
|
return contents.replace(/fill(?=="transparent"|="none"|:transparent|:none)/gi, 'filll')
|
||||||
|
.replace(/fill="[^"]+"/gi, `fill="${color}"`)
|
||||||
|
.replace(/fill:[^";]+/gi, `fill:${color};`)
|
||||||
|
.replace(/filll/gi, 'fill');
|
||||||
|
};
|
||||||
|
|
||||||
// Wrapper around image data. If not subclassed, it is used when loading in the area an image element for a drawing file (.json)
|
// 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.
|
// and it takes { displayName, contentType, base64, hash } as params.
|
||||||
var Image = new Lang.Class({
|
var Image = new Lang.Class({
|
||||||
|
|
@ -87,7 +109,7 @@ var Image = new Lang.Class({
|
||||||
return this.displayName;
|
return this.displayName;
|
||||||
},
|
},
|
||||||
|
|
||||||
toJson: function() {
|
toJSON: function() {
|
||||||
return {
|
return {
|
||||||
displayName: this.displayName,
|
displayName: this.displayName,
|
||||||
contentType: this.contentType,
|
contentType: this.contentType,
|
||||||
|
|
@ -112,6 +134,14 @@ var Image = new Lang.Class({
|
||||||
this._base64 = base64;
|
this._base64 = base64;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getBase64ForColor: function(color) {
|
||||||
|
if (!color || this.contentType != 'image/svg+xml')
|
||||||
|
return this.base64;
|
||||||
|
|
||||||
|
let contents = GLib.base64_decode(this.base64);
|
||||||
|
return GLib.base64_encode(replaceColor(contents, color));
|
||||||
|
},
|
||||||
|
|
||||||
// hash is not used
|
// hash is not used
|
||||||
get hash() {
|
get hash() {
|
||||||
if (!this._hash)
|
if (!this._hash)
|
||||||
|
|
@ -123,7 +153,22 @@ var Image = new Lang.Class({
|
||||||
this._hash = hash;
|
this._hash = hash;
|
||||||
},
|
},
|
||||||
|
|
||||||
get pixbuf() {
|
getBytesForColor: function(color) {
|
||||||
|
if (!color || this.contentType != 'image/svg+xml')
|
||||||
|
return this.bytes;
|
||||||
|
|
||||||
|
let contents = this.bytes.get_data();
|
||||||
|
return new GLib.Bytes(replaceColor(contents, color));
|
||||||
|
},
|
||||||
|
|
||||||
|
getPixbuf: function(color) {
|
||||||
|
if (color) {
|
||||||
|
let stream = Gio.MemoryInputStream.new_from_bytes(this.getBytesForColor(color));
|
||||||
|
let pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null);
|
||||||
|
stream.close(null);
|
||||||
|
return pixbuf;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._pixbuf) {
|
if (!this._pixbuf) {
|
||||||
let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes);
|
let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes);
|
||||||
this._pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null);
|
this._pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null);
|
||||||
|
|
@ -132,16 +177,16 @@ var Image = new Lang.Class({
|
||||||
return this._pixbuf;
|
return this._pixbuf;
|
||||||
},
|
},
|
||||||
|
|
||||||
getPixbufAtScale: function(width, height) {
|
getPixbufAtScale: function(width, height, color) {
|
||||||
let stream = Gio.MemoryInputStream.new_from_bytes(this.bytes);
|
let stream = Gio.MemoryInputStream.new_from_bytes(this.getBytesForColor(color));
|
||||||
let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, width, height, true, null);
|
let pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale(stream, width, height, true, null);
|
||||||
stream.close(null);
|
stream.close(null);
|
||||||
return pixbuf;
|
return pixbuf;
|
||||||
},
|
},
|
||||||
|
|
||||||
setCairoSource: function(cr, x, y, width, height, preserveAspectRatio) {
|
setCairoSource: function(cr, x, y, width, height, preserveAspectRatio, color) {
|
||||||
let pixbuf = preserveAspectRatio ? this.getPixbufAtScale(width, height)
|
let pixbuf = preserveAspectRatio ? this.getPixbufAtScale(width, height, color)
|
||||||
: this.pixbuf.scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR);
|
: this.getPixbuf(color).scale_simple(width, height, GdkPixbuf.InterpType.BILINEAR);
|
||||||
Gdk.cairo_set_source_pixbuf(cr, pixbuf, x, y);
|
Gdk.cairo_set_source_pixbuf(cr, pixbuf, x, y);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -542,7 +587,7 @@ var Jsons = {
|
||||||
|
|
||||||
var getDateString = function() {
|
var getDateString = function() {
|
||||||
let date = GLib.DateTime.new_now_local();
|
let date = GLib.DateTime.new_now_local();
|
||||||
return `${date.format("%F")} ${date.format("%X")}`;
|
return `${date.format("%F")} ${date.format("%T")}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
var saveSvg = function(content) {
|
var saveSvg = function(content) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
/* jslint esversion: 6 */
|
||||||
|
/* exported parseFile */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 ByteArray = imports.byteArray;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* [
|
||||||
|
* [
|
||||||
|
* 'palette name 1', // a palette for each column
|
||||||
|
* [
|
||||||
|
* 'rgb(...)',
|
||||||
|
* 'rgb(...):color display name', // the optional name separated with ':'
|
||||||
|
* ...
|
||||||
|
* ]
|
||||||
|
* ],
|
||||||
|
* [
|
||||||
|
* 'palette name 2',
|
||||||
|
* [...]
|
||||||
|
* ],
|
||||||
|
* ...
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parse(contents) {
|
||||||
|
let lines = contents.split('\n');
|
||||||
|
let line, name, columnNumber;
|
||||||
|
|
||||||
|
line = lines.shift();
|
||||||
|
if (!line || !line.startsWith('GIMP Palette'))
|
||||||
|
log("Missing magic header");
|
||||||
|
|
||||||
|
line = lines.shift();
|
||||||
|
if (line.startsWith('Name:')) {
|
||||||
|
name = line.slice(5).trim() || file.get_basename();
|
||||||
|
line = lines.shift();
|
||||||
|
}
|
||||||
|
if (line.startsWith('Columns:')) {
|
||||||
|
columnNumber = Number(line.slice(8).trim()) || 1;
|
||||||
|
line = lines.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
let columns = (new Array(columnNumber)).fill(null).map(() => []);
|
||||||
|
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
if (!line || line.startsWith('#'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
line = line.split('#')[0].trim();
|
||||||
|
|
||||||
|
let [, color, displayName] = line.split(/(^[\d\s]+)/);
|
||||||
|
|
||||||
|
let values = color.trim().split(/\D+/gi).filter(value => value >= 0 && value <= 255);
|
||||||
|
if (values.length < 3)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let string = `rgb(${values[0]},${values[1]},${values[2]})`;
|
||||||
|
if (displayName.trim())
|
||||||
|
string += `:${displayName.trim()}`;
|
||||||
|
|
||||||
|
columns[index % columns.length].push(string);
|
||||||
|
});
|
||||||
|
|
||||||
|
return columns.map((column, index) => [columnNumber > 1 ? `${name} ${index + 1}` : name, column]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFile(file) {
|
||||||
|
if (!file.query_exists(null))
|
||||||
|
return [];
|
||||||
|
|
||||||
|
let [, contents] = file.load_contents(null);
|
||||||
|
if (contents instanceof Uint8Array)
|
||||||
|
contents = ByteArray.toString(contents);
|
||||||
|
|
||||||
|
return parse(contents);
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Draw On Your Screen\n"
|
"Project-Id-Version: Draw On Your Screen\n"
|
||||||
"Report-Msgid-Bugs-To: https://framagit.org/abakkk/DrawOnYourScreen/issues\n"
|
"Report-Msgid-Bugs-To: https://framagit.org/abakkk/DrawOnYourScreen/issues\n"
|
||||||
"POT-Creation-Date: 2020-09-18 11:37+0200\n"
|
"POT-Creation-Date: 2020-09-19 15:32+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
@ -47,6 +47,11 @@ msgstr ""
|
||||||
msgid "Type your text and press <i>%s</i>"
|
msgid "Type your text and press <i>%s</i>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Translators: It is displayed in an OSD notification to ask the user to start picking, so it should use the imperative mood.
|
||||||
|
msgctxt "osd-notification"
|
||||||
|
msgid "Pick a color"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Translators: "released" as the opposite of "grabbed"
|
#. Translators: "released" as the opposite of "grabbed"
|
||||||
msgid "Keyboard and pointer released"
|
msgid "Keyboard and pointer released"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -284,6 +289,10 @@ msgstr ""
|
||||||
msgid "Color"
|
msgid "Color"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Translators: It is displayed in a menu button tooltip or as a shortcut action description, so it should NOT use the imperative mood.
|
||||||
|
msgid "Pick a color"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Add to images"
|
msgid "Add to images"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
38
menu.js
38
menu.js
|
|
@ -315,8 +315,6 @@ var DrawingMenu = new Lang.Class({
|
||||||
this.lineSection.actor.visible = !isText && !isImage;
|
this.lineSection.actor.visible = !isText && !isImage;
|
||||||
this.fontSection.actor.visible = isText;
|
this.fontSection.actor.visible = isText;
|
||||||
this.imageSection.actor.visible = isImage;
|
this.imageSection.actor.visible = isImage;
|
||||||
this.colorItem.setSensitive(!isImage);
|
|
||||||
this.paletteItem.setSensitive(!isImage);
|
|
||||||
this.fillItem.setSensitive(!isText && !isImage);
|
this.fillItem.setSensitive(!isText && !isImage);
|
||||||
this.fillSection.setSensitive(!isText && !isImage);
|
this.fillSection.setSensitive(!isText && !isImage);
|
||||||
|
|
||||||
|
|
@ -483,6 +481,17 @@ var DrawingMenu = new Lang.Class({
|
||||||
item.icon.set_gicon(icon);
|
item.icon.set_gicon(icon);
|
||||||
item.icon.set_style(`color:${this.area.currentColor.to_string().slice(0, 7)};`);
|
item.icon.set_style(`color:${this.area.currentColor.to_string().slice(0, 7)};`);
|
||||||
|
|
||||||
|
if (GS_VERSION >= '3.30') {
|
||||||
|
let colorPickerCallback = () => {
|
||||||
|
this.close();
|
||||||
|
this.area.pickColor();
|
||||||
|
};
|
||||||
|
// Translators: It is displayed in a menu button tooltip or as a shortcut action description, so it should NOT use the imperative mood.
|
||||||
|
let colorPickerButton = new ActionButton(_("Pick a color"), Files.Icons.COLOR_PICKER, colorPickerCallback, null, true);
|
||||||
|
let index = getActor(item).get_children().length - 1;
|
||||||
|
getActor(item).insert_child_at_index(colorPickerButton, index);
|
||||||
|
}
|
||||||
|
|
||||||
item.menu.itemActivated = item.menu.close;
|
item.menu.itemActivated = item.menu.close;
|
||||||
|
|
||||||
this._populateColorSubMenu();
|
this._populateColorSubMenu();
|
||||||
|
|
@ -512,6 +521,7 @@ var DrawingMenu = new Lang.Class({
|
||||||
item.icon.set_gicon(icon);
|
item.icon.set_gicon(icon);
|
||||||
|
|
||||||
item.menu.itemActivated = item.menu.close;
|
item.menu.itemActivated = item.menu.close;
|
||||||
|
item.menu.actor.add_style_class_name('draw-on-your-screen-menu-ellipsized');
|
||||||
|
|
||||||
item.menu.openOld = item.menu.open;
|
item.menu.openOld = item.menu.open;
|
||||||
item.menu.open = (animate) => {
|
item.menu.open = (animate) => {
|
||||||
|
|
@ -521,8 +531,12 @@ var DrawingMenu = new Lang.Class({
|
||||||
item.label.set_text(DisplayStrings.getFontFamily(family));
|
item.label.set_text(DisplayStrings.getFontFamily(family));
|
||||||
this.area.currentFontFamily = family;
|
this.area.currentFontFamily = family;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (FONT_FAMILY_STYLE)
|
if (FONT_FAMILY_STYLE)
|
||||||
subItem.label.set_style(`font-family:${family}`);
|
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||||
|
subItem.label.set_style(`font-family:${family}`);
|
||||||
|
});
|
||||||
|
|
||||||
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -541,6 +555,7 @@ var DrawingMenu = new Lang.Class({
|
||||||
item.update();
|
item.update();
|
||||||
|
|
||||||
item.menu.itemActivated = item.menu.close;
|
item.menu.itemActivated = item.menu.close;
|
||||||
|
item.menu.actor.add_style_class_name('draw-on-your-screen-menu-ellipsized');
|
||||||
|
|
||||||
item.menu.openOld = item.menu.open;
|
item.menu.openOld = item.menu.open;
|
||||||
item.menu.open = (animate) => {
|
item.menu.open = (animate) => {
|
||||||
|
|
@ -549,7 +564,13 @@ var DrawingMenu = new Lang.Class({
|
||||||
let subItem = item.menu.addAction(image.toString(), () => {
|
let subItem = item.menu.addAction(image.toString(), () => {
|
||||||
this.area.currentImage = image;
|
this.area.currentImage = image;
|
||||||
item.update();
|
item.update();
|
||||||
}, image.thumbnailGicon || undefined);
|
}, Files.Icons.FAKE);
|
||||||
|
|
||||||
|
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||||
|
if (subItem.setIcon && image.thumbnailGicon)
|
||||||
|
subItem.setIcon(image.thumbnailGicon);
|
||||||
|
});
|
||||||
|
|
||||||
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -563,6 +584,7 @@ var DrawingMenu = new Lang.Class({
|
||||||
_addDrawingNameItem: function(menu) {
|
_addDrawingNameItem: function(menu) {
|
||||||
this.drawingNameMenuItem = new PopupMenu.PopupMenuItem('', { reactive: false, activate: false });
|
this.drawingNameMenuItem = new PopupMenu.PopupMenuItem('', { reactive: false, activate: false });
|
||||||
this.drawingNameMenuItem.setSensitive(false);
|
this.drawingNameMenuItem.setSensitive(false);
|
||||||
|
getActor(this.drawingNameMenuItem).add_style_class_name('draw-on-your-screen-menu-ellipsized');
|
||||||
menu.addMenuItem(this.drawingNameMenuItem);
|
menu.addMenuItem(this.drawingNameMenuItem);
|
||||||
this._updateDrawingNameMenuItem();
|
this._updateDrawingNameMenuItem();
|
||||||
},
|
},
|
||||||
|
|
@ -584,6 +606,7 @@ var DrawingMenu = new Lang.Class({
|
||||||
item.icon.set_icon_name(icon);
|
item.icon.set_icon_name(icon);
|
||||||
|
|
||||||
item.menu.itemActivated = item.menu.close;
|
item.menu.itemActivated = item.menu.close;
|
||||||
|
item.menu.actor.add_style_class_name('draw-on-your-screen-menu-ellipsized');
|
||||||
|
|
||||||
item.menu.openOld = item.menu.open;
|
item.menu.openOld = item.menu.open;
|
||||||
item.menu.open = (animate) => {
|
item.menu.open = (animate) => {
|
||||||
|
|
@ -605,7 +628,12 @@ var DrawingMenu = new Lang.Class({
|
||||||
this.area.loadJson(json);
|
this.area.loadJson(json);
|
||||||
this._updateDrawingNameMenuItem();
|
this._updateDrawingNameMenuItem();
|
||||||
this._updateActionSensitivity();
|
this._updateActionSensitivity();
|
||||||
}, json.gicon);
|
}, Files.Icons.FAKE);
|
||||||
|
|
||||||
|
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
||||||
|
if (subItem.setIcon)
|
||||||
|
subItem.setIcon(json.gicon);
|
||||||
|
});
|
||||||
|
|
||||||
subItem.label.get_clutter_text().set_use_markup(true);
|
subItem.label.get_clutter_text().set_use_markup(true);
|
||||||
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
getActor(subItem).connect('key-focus-in', updateSubMenuAdjustment);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
"3.30",
|
"3.30",
|
||||||
"3.32",
|
"3.32",
|
||||||
"3.34",
|
"3.34",
|
||||||
"3.36"
|
"3.36",
|
||||||
|
"3.38"
|
||||||
],
|
],
|
||||||
"version": 6.4
|
"version": 7
|
||||||
}
|
}
|
||||||
|
|
|
||||||
53
prefs.js
53
prefs.js
|
|
@ -31,6 +31,7 @@ const Gtk = imports.gi.Gtk;
|
||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = ExtensionUtils.getCurrentExtension();
|
const Me = ExtensionUtils.getCurrentExtension();
|
||||||
const Convenience = ExtensionUtils.getSettings && ExtensionUtils.initTranslations ? ExtensionUtils : Me.imports.convenience;
|
const Convenience = ExtensionUtils.getSettings && ExtensionUtils.initTranslations ? ExtensionUtils : Me.imports.convenience;
|
||||||
|
const GimpPaletteParser = Me.imports.gimpPaletteParser;
|
||||||
const Shortcuts = Me.imports.shortcuts;
|
const Shortcuts = Me.imports.shortcuts;
|
||||||
const gettext = imports.gettext.domain(Me.metadata['gettext-domain']).gettext;
|
const gettext = imports.gettext.domain(Me.metadata['gettext-domain']).gettext;
|
||||||
const _ = function(string) {
|
const _ = function(string) {
|
||||||
|
|
@ -150,26 +151,32 @@ const DrawingPage = new GObject.Class({
|
||||||
|
|
||||||
let palettesFrame = new Frame({ label: _("Palettes") });
|
let palettesFrame = new Frame({ label: _("Palettes") });
|
||||||
box.add(palettesFrame);
|
box.add(palettesFrame);
|
||||||
|
let palettesFrameBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
|
||||||
|
palettesFrame.add(palettesFrameBox);
|
||||||
|
|
||||||
let palettesScrolledWindow = new Gtk.ScrolledWindow({ vscrollbar_policy: Gtk.PolicyType.NEVER, margin_top: MARGIN / 2, margin_bottom: MARGIN / 2 });
|
let palettesScrolledWindow = new Gtk.ScrolledWindow({ vscrollbar_policy: Gtk.PolicyType.NEVER });
|
||||||
palettesFrame.add(palettesScrolledWindow);
|
palettesFrameBox.add(palettesScrolledWindow);
|
||||||
|
let palettesViewport = new Gtk.Viewport({ margin_top: MARGIN / 2, margin_bottom: MARGIN / 2 });
|
||||||
|
palettesScrolledWindow.add(palettesViewport);
|
||||||
this.palettesListBox = new Gtk.ListBox({ selection_mode: 0, hexpand: true });
|
this.palettesListBox = new Gtk.ListBox({ selection_mode: 0, hexpand: true });
|
||||||
this.palettesListBox.get_style_context().add_class('background');
|
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_name(this.schema.get_key('palettes').get_summary());
|
||||||
this.palettesListBox.get_accessible().set_description(this.schema.get_key('palettes').get_description());
|
this.palettesListBox.get_accessible().set_description(this.schema.get_key('palettes').get_description());
|
||||||
palettesScrolledWindow.add(this.palettesListBox);
|
palettesViewport.add(this.palettesListBox);
|
||||||
|
|
||||||
this.settings.connect('changed::palettes', this._updatePalettes.bind(this));
|
this.settings.connect('changed::palettes', this._updatePalettes.bind(this));
|
||||||
this._updatePalettes();
|
this._updatePalettes();
|
||||||
|
|
||||||
this.addBox = new Gtk.Box(ROWBOX_MARGIN_PARAMS);
|
let 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);
|
let addButton = Gtk.Button.new_from_icon_name('list-add-symbolic', Gtk.IconSize.BUTTON);
|
||||||
addButton.set_tooltip_text(_("Add a new palette"));
|
addButton.set_tooltip_text(_("Add a new palette"));
|
||||||
this.addBox.pack_start(addButton, true, true, 4);
|
addBox.pack_start(addButton, true, true, 4);
|
||||||
addButton.connect('clicked', this._addNewPalette.bind(this));
|
addButton.connect('clicked', this._addNewPalette.bind(this));
|
||||||
this.palettesListBox.add(this.addBox);
|
let importButton = Gtk.Button.new_from_icon_name('document-open-symbolic', Gtk.IconSize.BUTTON);
|
||||||
this.addBox.get_parent().set_activatable(false);
|
importButton.set_tooltip_text(_GTK("Select a File"));
|
||||||
|
addBox.pack_start(importButton, true, true, 4);
|
||||||
|
importButton.connect('clicked', this._importPalette.bind(this));
|
||||||
|
palettesFrameBox.add(addBox);
|
||||||
|
|
||||||
let areaFrame = new Frame({ label: _("Area") });
|
let areaFrame = new Frame({ label: _("Area") });
|
||||||
box.add(areaFrame);
|
box.add(areaFrame);
|
||||||
|
|
@ -285,10 +292,9 @@ const DrawingPage = new GObject.Class({
|
||||||
|
|
||||||
_updatePalettes: function() {
|
_updatePalettes: function() {
|
||||||
this.palettes = this.settings.get_value('palettes').deep_unpack();
|
this.palettes = this.settings.get_value('palettes').deep_unpack();
|
||||||
this.palettesListBox.get_children().filter(row=> row.get_child() != this.addBox)
|
this.palettesListBox.get_children().slice(this.palettes.length)
|
||||||
.slice(this.palettes.length)
|
|
||||||
.forEach(row => this.palettesListBox.remove(row));
|
.forEach(row => this.palettesListBox.remove(row));
|
||||||
let paletteBoxes = this.palettesListBox.get_children().map(row => row.get_child()).filter(child => child != this.addBox);
|
let paletteBoxes = this.palettesListBox.get_children().map(row => row.get_child());
|
||||||
|
|
||||||
this.palettes.forEach((palette, paletteIndex) => {
|
this.palettes.forEach((palette, paletteIndex) => {
|
||||||
let [name, colors] = palette;
|
let [name, colors] = palette;
|
||||||
|
|
@ -316,17 +322,20 @@ const DrawingPage = new GObject.Class({
|
||||||
paletteBox.get_parent().set_activatable(false);
|
paletteBox.get_parent().set_activatable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
colors.splice(9);
|
|
||||||
while (colors.length < 9)
|
while (colors.length < 9)
|
||||||
colors.push('transparent');
|
colors.push('transparent');
|
||||||
|
|
||||||
let colorsBox = paletteBox.get_children()[1];
|
let colorsBox = paletteBox.get_children()[1];
|
||||||
let colorButtons = colorsBox.get_children();
|
let colorButtons = colorsBox.get_children();
|
||||||
colors.forEach((color, colorIndex) => {
|
colors.forEach((color, colorIndex) => {
|
||||||
|
let [colorString, displayName] = color.split(':');
|
||||||
if (colorButtons[colorIndex]) {
|
if (colorButtons[colorIndex]) {
|
||||||
colorButtons[colorIndex].color_string = color;
|
colorButtons[colorIndex].color_string = colorString;
|
||||||
|
colorButtons[colorIndex].tooltip_text = displayName || null;
|
||||||
} else {
|
} else {
|
||||||
let colorButton = new ColorStringButton({ color_string: color, use_alpha: true, show_editor: true, halign: Gtk.Align.START, hexpand: false });
|
let colorButton = new ColorStringButton({ color_string: colorString, tooltip_text: displayName || null,
|
||||||
|
use_alpha: true, show_editor: true,
|
||||||
|
halign: Gtk.Align.START, hexpand: false });
|
||||||
colorButton.connect('notify::color-string', this._onPaletteColorChanged.bind(this, paletteIndex, colorIndex));
|
colorButton.connect('notify::color-string', this._onPaletteColorChanged.bind(this, paletteIndex, colorIndex));
|
||||||
colorsBox.add(colorButton);
|
colorsBox.add(colorButton);
|
||||||
}
|
}
|
||||||
|
|
@ -347,6 +356,8 @@ const DrawingPage = new GObject.Class({
|
||||||
|
|
||||||
_onPaletteColorChanged: function(paletteIndex, colorIndex, colorButton) {
|
_onPaletteColorChanged: function(paletteIndex, colorIndex, colorButton) {
|
||||||
this.palettes[paletteIndex][1][colorIndex] = colorButton.get_rgba().to_string();
|
this.palettes[paletteIndex][1][colorIndex] = colorButton.get_rgba().to_string();
|
||||||
|
if (colorButton.tooltip_text)
|
||||||
|
this.palettes[paletteIndex][1][colorIndex] += `:${colorButton.tooltip_text}`;
|
||||||
this._savePalettes();
|
this._savePalettes();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -357,6 +368,20 @@ const DrawingPage = new GObject.Class({
|
||||||
this._savePalettes();
|
this._savePalettes();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_importPalette: function() {
|
||||||
|
let dialog = new Gtk.FileChooserNative({ action: Gtk.FileChooserAction.OPEN, title: _GTK("Select a File") });
|
||||||
|
let filter = new Gtk.FileFilter();
|
||||||
|
filter.set_name("GIMP Palette (*.gpl)");
|
||||||
|
filter.add_pattern('*.gpl');
|
||||||
|
dialog.add_filter(filter);
|
||||||
|
if (dialog.run() == Gtk.ResponseType.ACCEPT) {
|
||||||
|
let file = dialog.get_file();
|
||||||
|
let palettes = GimpPaletteParser.parseFile(file);
|
||||||
|
palettes.forEach(palette => this.palettes.push(palette));
|
||||||
|
this._savePalettes();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_removePalette: function(paletteIndex) {
|
_removePalette: function(paletteIndex) {
|
||||||
this.palettes.splice(paletteIndex, 1);
|
this.palettes.splice(paletteIndex, 1);
|
||||||
this._savePalettes();
|
this._savePalettes();
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -100,12 +100,12 @@
|
||||||
<key type="a(sas)" name="palettes">
|
<key type="a(sas)" name="palettes">
|
||||||
<default>
|
<default>
|
||||||
[
|
[
|
||||||
("Palette", ["HotPink","Cyan","yellow","Orangered","Chartreuse","DarkViolet","White","Gray","Black"]),
|
('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 lighter', ['rgb(153,193,241):Blue 1', 'rgb(143,240,164):Green 1', 'rgb(249,240,107):Yellow 1', 'rgb(255,190,111):Orange 1', 'rgb(246,97,81):Red 1', 'rgb(220,138,221):Purple 1', 'rgb(205,171,143):Brown 1', 'rgb(255,255,255):Light 1', 'rgb(119,118,123):Dark 1']),
|
||||||
("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 light', ['rgb(98,160,234):Blue 2', 'rgb(87,227,137):Green 2', 'rgb(248,228,92):Yellow 2', 'rgb(255,163,72):Orange 2', 'rgb(237,51,59):Red 2', 'rgb(192,97,203):Purple 2', 'rgb(181,131,90):Brown 2', 'rgb(246,245,244):Light 2', 'rgb(94,92,100):Dark 2']),
|
||||||
("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 normal', ['rgb(53,132,228):Blue 3', 'rgb(51,209,122):Green 3', 'rgb(246,211,45):Yellow 3', 'rgb(255,120,0):Orange 3', 'rgb(224,27,36):Red 3', 'rgb(145,65,172):Purple 3', 'rgb(152,106,68):Brown 3', 'rgb(222,221,218):Light 3', 'rgb(61,56,70):Dark 3']),
|
||||||
("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 dark', ['rgb(28,113,216):Blue 4', 'rgb(46,194,126):Green 4', 'rgb(245,194,17):Yellow 4', 'rgb(230,97,0):Orange 4', 'rgb(192,28,40):Red 4', 'rgb(129,61,156):Purple 4', 'rgb(134,94,60):Brown 4', 'rgb(192,191,188):Light 4', 'rgb(36,31,49):Dark 4']),
|
||||||
("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)"])
|
('GNOME HIG darker', ['rgb(26,95,180):Blue 5', 'rgb(38,162,105):Green 5', 'rgb(229,165,10):Yellow 5', 'rgb(198,70,0):Orange 5', 'rgb(165,29,45):Red 5', 'rgb(97,53,131):Purple 5', 'rgb(99,69,44):Brown 5', 'rgb(154,153,150):Light 5', 'rgb(0,0,0):Dark 5'])
|
||||||
]
|
]
|
||||||
</default>
|
</default>
|
||||||
<summary>Color palettes</summary>
|
<summary>Color palettes</summary>
|
||||||
|
|
@ -164,6 +164,10 @@
|
||||||
<default>["<Primary>v"]</default>
|
<default>["<Primary>v"]</default>
|
||||||
<summary>Add images from the clipboard</summary>
|
<summary>Add images from the clipboard</summary>
|
||||||
</key>
|
</key>
|
||||||
|
<key type="as" name="pick-color">
|
||||||
|
<default><![CDATA[['<Primary>KP_0','<Primary>0']]]></default>
|
||||||
|
<summary>Pick a color</summary>
|
||||||
|
</key>
|
||||||
<key type="as" name="redo">
|
<key type="as" name="redo">
|
||||||
<default>["<Primary><Shift>z"]</default>
|
<default>["<Primary><Shift>z"]</default>
|
||||||
<summary>Redo last brushstroke</summary>
|
<summary>Redo last brushstroke</summary>
|
||||||
|
|
|
||||||
11
shortcuts.js
11
shortcuts.js
|
|
@ -46,7 +46,7 @@ var INTERNAL_KEYBINDINGS = [
|
||||||
['undo', 'redo', 'delete-last-element', 'smooth-last-element'],
|
['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-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'],
|
'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'],
|
['switch-fill', 'switch-fill-rule', 'switch-color-palette', 'switch-color-palette-reverse', 'pick-color'],
|
||||||
['increment-line-width', 'increment-line-width-more', 'decrement-line-width', 'decrement-line-width-more',
|
['increment-line-width', 'increment-line-width-more', 'decrement-line-width', 'decrement-line-width-more',
|
||||||
'switch-linejoin', 'switch-linecap', 'switch-dash'],
|
'switch-linejoin', 'switch-linecap', 'switch-dash'],
|
||||||
['switch-font-family', 'switch-font-family-reverse', 'switch-font-weight', 'switch-font-style', 'switch-text-alignment'],
|
['switch-font-family', 'switch-font-family-reverse', 'switch-font-weight', 'switch-font-style', 'switch-text-alignment'],
|
||||||
|
|
@ -55,6 +55,15 @@ var INTERNAL_KEYBINDINGS = [
|
||||||
['open-next-json', 'open-previous-json', 'save-as-json', 'export-to-svg', 'open-preferences', 'toggle-help'],
|
['open-next-json', 'open-previous-json', 'save-as-json', 'export-to-svg', 'open-preferences', 'toggle-help'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (GS_VERSION < '3.30') {
|
||||||
|
// Remove 'pick-color' keybinding.
|
||||||
|
INTERNAL_KEYBINDINGS.forEach(settingKeys => {
|
||||||
|
let index = settingKeys.indexOf('pick-color');
|
||||||
|
if (index != -1)
|
||||||
|
settingKeys.splice(index, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (GS_VERSION < '3.36') {
|
if (GS_VERSION < '3.36') {
|
||||||
// Remove 'open-preferences' keybinding.
|
// Remove 'open-preferences' keybinding.
|
||||||
INTERNAL_KEYBINDINGS.forEach(settingKeys => {
|
INTERNAL_KEYBINDINGS.forEach(settingKeys => {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
|
|
||||||
.draw-on-your-screen-menu {
|
.draw-on-your-screen-menu {
|
||||||
font-size: 0.97em; /* default: 1em */
|
font-size: 0.97em; /* default: 1em */
|
||||||
|
min-width: 21em; /* like the aggregate menu */
|
||||||
}
|
}
|
||||||
|
|
||||||
.draw-on-your-screen-menu .popup-menu-item {
|
.draw-on-your-screen-menu .popup-menu-item {
|
||||||
|
|
@ -49,6 +50,11 @@
|
||||||
padding-top: 0.03em;
|
padding-top: 0.03em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* max size for uncontrolled contents (font, image and drawing names) */
|
||||||
|
.draw-on-your-screen-menu-ellipsized {
|
||||||
|
max-width: 21em;
|
||||||
|
}
|
||||||
|
|
||||||
.draw-on-your-screen-menu .toggle-switch {
|
.draw-on-your-screen-menu .toggle-switch {
|
||||||
height: 1.35em; /* default: 22px */
|
height: 1.35em; /* default: 22px */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue