diff --git a/area.js b/area.js index f2d9c34..f7b5e4c 100644 --- a/area.js +++ b/area.js @@ -45,6 +45,7 @@ const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext; const CAIRO_DEBUG_EXTENDS = false; const SVG_DEBUG_EXTENDS = false; const TEXT_CURSOR_TIME = 600; // ms +const GRID_TILES_HORIZONTAL_NUMBER = 30; const { Shapes, ShapeNames, Transformations, LineCapNames, LineJoinNames, FillRuleNames, FontWeightNames, FontStyleNames, FontStretchNames, FontVariantNames } = Elements; @@ -55,6 +56,16 @@ var ToolNames = Object.assign({}, ShapeNames, ManipulationNames); var FontGenericFamilies = ['Sans-Serif', 'Serif', 'Monospace', 'Cursive', 'Fantasy']; +const getClutterColorFromString = function(string, fallback) { + let [success, color] = Clutter.Color.from_string(string); + color.string = string; + if (success) + return color; + + log(`${Me.metadata.uuid}: "${string}" color cannot be parsed.`); + return Clutter.Color.get_static(Clutter.StaticColor[fallback]); +}; + // DrawingArea is the widget in which we draw, thanks to Cairo. // It creates and manages a DrawingElement for each "brushstroke". // It handles pointer/mouse/(touch?) events and some keyboard events. @@ -68,28 +79,39 @@ var DrawingArea = new Lang.Class({ _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.monitor = monitor; this.helper = helper; this.elements = []; this.undoneElements = []; + this.defaultFontFamily = 'Cantarell'; this.currentElement = null; this.currentTool = Shapes.NONE; this.currentImage = 0; + this.currentFontFamily = this.defaultFontFamily; + this.currentFontStyle = Pango.Style.NORMAL; + this.currentFontWeight = Pango.Weight.NORMAL; + this.currentFontStretch = Pango.Stretch.NORMAL; + this.currentFontVariant = Pango.Variant.NORMAL; + this.currentTextRightAligned = false; + 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(); }, @@ -121,6 +143,17 @@ 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 hasManipulationTool() { // No Object.values method in GS 3.24. return Object.keys(Manipulations).map(key => Manipulations[key]).indexOf(this.currentTool) != -1; @@ -142,20 +175,12 @@ var DrawingArea = new Lang.Class({ 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; + return family != this.defaultFontFamily && FontGenericFamilies.indexOf(family) == -1; }); - this._fontFamilies = [this.currentThemeFontFamily].concat(FontGenericFamilies, pangoFontFamilies); + this._fontFamilies = [this.defaultFontFamily].concat(FontGenericFamilies, pangoFontFamilies); } return this._fontFamilies; }, @@ -179,57 +204,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 = Math.max(64, 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('area-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 = Math.max(1, Me.drawingSettings.get_uint('grid-line-spacing')); + this.gridLineWidth = Math.round(Math.max(0.1, 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(Math.max(0, Me.drawingSettings.get_double('dash-array-on')) * 100) / 100; + let off = Math.round(Math.max(0, 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) { @@ -267,27 +279,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(); @@ -833,7 +845,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() { @@ -844,10 +856,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); @@ -856,18 +866,17 @@ 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._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', null, this.currentColor.string || this.currentColor.to_string(), this.currentColor.to_string().slice(0, 7), -1, false); }, selectTool: function(tool) { @@ -881,6 +890,20 @@ var DrawingArea = new Lang.Class({ this.emit('show-osd', null, this.fill ? _("Fill") : _("Outline"), "", -1, false); }, + switchFillRule: function() { + this.currentFillRule = this.currentFillRule == 1 ? 0 : this.currentFillRule + 1; + this.emit('show-osd', null, _(FillRuleNames[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', null, this.currentPalette[0], "", -1, false); + }, + switchDash: function() { this.dashedLine = !this.dashedLine; this.emit('show-osd', null, this.dashedLine ? _("Dashed line") : _("Full line"), "", -1, false); @@ -901,11 +924,6 @@ var DrawingArea = new Lang.Class({ this.emit('show-osd', null, _(LineCapNames[this.currentLineCap]), "", -1, false); }, - switchFillRule: function() { - this.currentFillRule = this.currentFillRule == 1 ? 0 : this.currentFillRule + 1; - this.emit('show-osd', null, _(FillRuleNames[this.currentFillRule]), "", -1, false); - }, - switchFontWeight: function() { let fontWeights = Object.keys(FontWeightNames).map(key => Number(key)); let index = fontWeights.indexOf(this.currentFontWeight); @@ -982,7 +1000,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(); @@ -999,8 +1017,7 @@ 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) { @@ -1060,7 +1077,7 @@ var DrawingArea = new Lang.Class({ let content = ``; if (SVG_DEBUG_EXTENDS) content = ``; - let backgroundColorString = this.hasBackground ? this.activeBackgroundColor.to_string() : 'transparent'; + let backgroundColorString = this.hasBackground ? this.areaBackgroundColor.to_string() : 'transparent'; if (backgroundColorString != 'transparent') { content += `\n `; } diff --git a/data/default.css b/data/default.css deleted file mode 100644 index 4a03e08..0000000 --- a/data/default.css +++ /dev/null @@ -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); -} -*/ diff --git a/data/icons/palette-symbolic.svg b/data/icons/palette-symbolic.svg new file mode 100644 index 0000000..8a4174b --- /dev/null +++ b/data/icons/palette-symbolic.svg @@ -0,0 +1,32 @@ + + + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 +https://svgsilh.com/image/2026954.html +https://creativecommons.org/publicdomain/zero/1.0/ + + + + + diff --git a/extension.js b/extension.js index 632cb8e..d12d1bd 100644 --- a/extension.js +++ b/extension.js @@ -21,7 +21,6 @@ */ const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; const Lang = imports.lang; const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; @@ -57,6 +56,7 @@ function init() { function enable() { Me.settings = Convenience.getSettings(); Me.internalShortcutSettings = Convenience.getSettings(Me.metadata['settings-schema'] + '.internal-shortcuts'); + Me.drawingSettings = Convenience.getSettings(Me.metadata['settings-schema'] + '.drawing'); manager = new AreaManager(); } @@ -105,26 +105,6 @@ var AreaManager = new Lang.Class({ this.desktopSettingHandler = Me.settings.connect('changed::drawing-on-desktop', this.onDesktopSettingChanged.bind(this)); this.persistentSettingHandler = Me.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); - }); }, onDesktopSettingChanged: function() { @@ -218,14 +198,15 @@ var AreaManager = new Lang.Class({ '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) }; @@ -251,7 +232,7 @@ var AreaManager = new Lang.Class({ Me.internalShortcutSettings, Meta.KeyBindingFlags.NONE, DRAWING_ACTION_MODE | WRITING_ACTION_MODE, - () => this.activeArea.selectColor(iCaptured)); + this.activeArea.selectColor.bind(this.activeArea, iCaptured - 1)); } }, @@ -275,23 +256,6 @@ 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() { for (let i = 0; i < this.areas.length; i++) this.areas[i].erase(); @@ -521,14 +485,6 @@ 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; diff --git a/locale/draw-on-your-screen.pot b/locale/draw-on-your-screen.pot index 15ef699..833681d 100644 --- a/locale/draw-on-your-screen.pot +++ b/locale/draw-on-your-screen.pot @@ -47,6 +47,90 @@ msgstr "" msgid "translator-credits" msgstr "" +msgid "Drawing" +msgstr "" + +msgid "Palettes" +msgstr "" + +msgid "The palettes of drawing colors" +msgstr "" + +msgid "Palette" +msgstr "" + +msgid "Add a new palette" +msgstr "" + +msgid "New palette" +msgstr "" + +msgid "Rename the palette" +msgstr "" + +msgid "Remove the palette" +msgstr "" + +msgid "Auto" +msgstr "" + +msgid "Area" +msgstr "" + +msgid "Square area size" +msgstr "" + +msgid "Compute the size of the square area from the screen size" +msgstr "" + +msgid "The size of the square area in pixels" +msgstr "" + +msgid "Background color" +msgstr "" + +msgid "The color of the drawing area background" +msgstr "" + +msgid "Grid overlay line" +msgstr "" + +msgid "Compute the lengths from the screen size" +msgstr "" + +msgid "The line width in pixels" +msgstr "" + +msgid "The gap between lines in pixels" +msgstr "" + +msgid "Grid overlay color" +msgstr "" + +msgid "The color of the lines" +msgstr "" + +msgid "Tools" +msgstr "" + +msgid "Dash array" +msgstr "" + +msgid "Compute the lengths from the line width" +msgstr "" + +msgid "The dash length in pixels" +msgstr "" + +msgid "The gap between the dashes in pixels" +msgstr "" + +msgid "Dash offset" +msgstr "" + +msgid "The dash offset in pixels" +msgstr "" + msgid "Preferences" msgstr "" @@ -197,6 +281,15 @@ msgstr "" msgid "Toggle fill/outline" msgstr "" +msgid "Toggle fill rule" +msgstr "" + +msgid "Change color palette" +msgstr "" + +msgid "Change color palette (reverse)" +msgstr "" + msgid "Increment line width" msgstr "" @@ -215,9 +308,6 @@ msgstr "" msgid "Change linecap" msgstr "" -msgid "Toggle fill rule" -msgstr "" - msgid "Change font family" msgstr "" @@ -257,20 +347,12 @@ msgstr "" msgid "Save drawing as a SVG file" msgstr "" -msgid "Edit style" -msgstr "" - msgid "Open preferences" msgstr "" msgid "Show help" msgstr "" -msgid "" -"Default drawing style attributes (color palette, font, line, dash) are defined in an editable css file.\n" -"See ā€œ%sā€." -msgstr "" - msgid "" "When you save elements made with eraser in a SVG file, " "they are colored with background color, transparent if it is disabled.\n" diff --git a/menu.js b/menu.js index 911f082..666279a 100644 --- a/menu.js +++ b/menu.js @@ -46,6 +46,7 @@ const GS_VERSION = Config.PACKAGE_VERSION; const ICON_DIR = Me.dir.get_child('data').get_child('icons'); const SMOOTH_ICON_PATH = ICON_DIR.get_child('smooth-symbolic.svg').get_path(); +const PALETTE_ICON_PATH = ICON_DIR.get_child('palette-symbolic.svg').get_path(); const COLOR_ICON_PATH = ICON_DIR.get_child('color-symbolic.svg').get_path(); const FILL_ICON_PATH = ICON_DIR.get_child('fill-symbolic.svg').get_path(); const STROKE_ICON_PATH = ICON_DIR.get_child('stroke-symbolic.svg').get_path(); @@ -95,6 +96,7 @@ var DrawingMenu = new Lang.Class({ menuCloseFunc.bind(this.menu)(animate); }; + this.paletteIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(PALETTE_ICON_PATH) }); this.colorIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(COLOR_ICON_PATH) }); this.smoothIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(SMOOTH_ICON_PATH) }); this.strokeIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(STROKE_ICON_PATH) }); @@ -164,6 +166,7 @@ var DrawingMenu = new Lang.Class({ this._addSeparator(this.menu, true); this._addSubMenuItem(this.menu, 'document-edit-symbolic', Area.ToolNames, this.area, 'currentTool', this._updateSectionVisibility.bind(this)); + this.paletteItem = this._addPaletteSubMenuItem(this.menu); this.colorItem = this._addColorSubMenuItem(this.menu); this.fillItem = this._addSwitchItem(this.menu, _("Fill"), this.strokeIcon, this.fillIcon, this.area, 'fill', this._updateSectionVisibility.bind(this)); this.fillSection = new PopupMenu.PopupMenuSection(); @@ -216,7 +219,7 @@ var DrawingMenu = new Lang.Class({ this._addSaveDrawingSubMenuItem(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(_("Open preferences"), manager.openPreferences.bind(manager), 'document-page-setup-symbolic'); this.menu.addAction(_("Show help"), () => { this.close(); this.area.toggleHelp(); }, 'preferences-desktop-keyboard-shortcuts-symbolic'); this._updateActionSensitivity(); @@ -254,6 +257,7 @@ var DrawingMenu = new Lang.Class({ 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); @@ -374,8 +378,37 @@ var DrawingMenu = new Lang.Class({ menu.addMenuItem(item); }, + _addPaletteSubMenuItem: function(menu) { + let text = _(this.area.currentPalette[0] || "Palette"); + let item = new PopupMenu.PopupSubMenuMenuItem(text, true); + item.icon.set_gicon(this.paletteIcon); + + 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) { let item = new PopupMenu.PopupSubMenuMenuItem(_("Color"), true); + this.colorSubMenu = item.menu; item.icon.set_gicon(this.colorIcon); item.icon.set_style(`color:${this.area.currentColor.to_string().slice(0, 7)};`); @@ -383,24 +416,28 @@ var DrawingMenu = new Lang.Class({ 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; - }); + 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 = color.string || color.to_string(); + 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(this.area.currentFontFamily, true); item.icon.set_icon_name(icon); diff --git a/prefs.js b/prefs.js index 3706a50..cbf1d1e 100644 --- a/prefs.js +++ b/prefs.js @@ -20,16 +20,21 @@ * along with this program. If not, see . */ +const Gdk = imports.gi.Gdk; 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 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; @@ -61,6 +66,8 @@ var INTERNAL_KEYBINDINGS = { '-separator-2': '', 'switch-fill': "Toggle fill/outline", 'switch-fill-rule': "Toggle fill rule", + 'switch-color-palette': "Change color palette", + 'switch-color-palette-reverse': "Change color palette (reverse)", '-separator-3': '', 'increment-line-width': "Increment line width", 'decrement-line-width': "Decrement line width", @@ -71,7 +78,7 @@ var INTERNAL_KEYBINDINGS = { 'switch-dash': "Dashed line", '-separator-4': '', 'switch-font-family': "Change font family", - 'reverse-switch-font-family': "Change font family (reverse)", + 'switch-font-family-reverse': "Change font family (reverse)", 'switch-font-weight': "Change font weight", 'switch-font-style': "Change font style", 'switch-text-alignment': "Toggle text alignment", @@ -86,7 +93,6 @@ var INTERNAL_KEYBINDINGS = { '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" }; @@ -128,7 +134,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; @@ -147,6 +152,8 @@ const TopStack = new GObject.Class({ this.parent({ transition_type: 1, transition_duration: 500, expand: true }); this.prefsPage = new PrefsPage(); this.add_titled(this.prefsPage, 'prefs', _("Preferences")); + this.drawingPage = new DrawingPage(); + this.add_titled(this.drawingPage, 'drawing', _("Drawing")); this.aboutPage = new AboutPage(); this.add_titled(this.aboutPage, 'about', _("About")); } @@ -158,7 +165,7 @@ 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 }); this.add(vbox); @@ -199,13 +206,213 @@ const AboutPage = new GObject.Class({ }); +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: MARGIN*3 }); + this.add(box); + + let palettesFrame = new Gtk.Frame({ label_yalign: 1.0 }); + palettesFrame.set_label_widget(new Gtk.Label({ margin_bottom: MARGIN/2, use_markup: true, 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'); + palettesScrolledWindow.add(this.palettesListBox); + + this.settings.connect('changed::palettes', this._updatePalettes.bind(this)); + this._updatePalettes(); + + this.addBox = new Gtk.Box({ margin_top: MARGIN/2, margin_bottom: MARGIN/2, margin_left: MARGIN, margin_right: MARGIN, tooltip_text: _("Add a new palette") }); + let addButton = Gtk.Button.new_from_icon_name('list-add-symbolic', Gtk.IconSize.BUTTON); + 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 Gtk.Frame({ margin_top: 3*MARGIN, label_yalign: 1.0 }); + areaFrame.set_label_widget(new Gtk.Label({ margin_bottom: MARGIN/2, use_markup: true, 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: _("Square area size") }); + let squareAreaAutoButton = new Gtk.CheckButton({ label: _("Auto"), tooltip_text: _(this.schema.get_key('square-area-auto').get_description()) }); + let squareAreaSizeButton = new PixelSpinButton({ width_chars: 5, digits: 0, + adjustment: Gtk.Adjustment.new(0, 64, 32768, 1, 10, 0), + 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: _("Background color") }); + let backgroundColorButton = new ColorStringButton({ use_alpha: true, show_editor: true, + tooltip_text: _(this.schema.get_key('area-background-color').get_description()) }); + this.settings.bind('area-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"), tooltip_text: _(this.schema.get_key('grid-line-auto').get_description()) }); + let gridLineWidthButton = new PixelSpinButton({ width_chars: 5, digits: 1, + adjustment: Gtk.Adjustment.new(0, 0.1, 10, 0.1, 1, 0), + tooltip_text: _(this.schema.get_key('grid-line-width').get_description()) }); + let gridLineSpacingButton = new PixelSpinButton({ width_chars: 5, digits: 1, + adjustment: Gtk.Adjustment.new(0, 1, 16384, 1, 10, 0), + 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: _("Grid overlay color") }); + let gridColorButton = new ColorStringButton({ use_alpha: true, show_editor: true, + 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 Gtk.Frame({ margin_top: 3*MARGIN, label_yalign: 1.0 }); + toolsFrame.set_label_widget(new Gtk.Label({ margin_bottom: MARGIN/2, use_markup: true, 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"), tooltip_text: _(this.schema.get_key('dash-array-auto').get_description()) }); + let dashArrayOnButton = new PixelSpinButton({ width_chars: 5, digits: 1, + adjustment: Gtk.Adjustment.new(0, 0, 16384, 0.1, 1, 0), + tooltip_text: _(this.schema.get_key('dash-array-on').get_description()) }); + let dashArrayOffButton = new PixelSpinButton({ width_chars: 5, digits: 1, + adjustment: Gtk.Adjustment.new(0, 0, 16384, 0.1, 1, 0), + 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: _("Dash offset") }); + let dashOffsetButton = new PixelSpinButton({ width_chars: 5, digits: 1, + adjustment: Gtk.Adjustment.new(0, -16384, 16384, 0.1, 1, 0), + tooltip_text: _(this.schema.get_key('dash-offset').get_description()) }); + this.settings.bind('dash-offset', dashOffsetButton, 'value', 0); + dashOffsetRow.addWidget(dashOffsetButton); + toolsListBox.add(dashOffsetRow); + }, + + _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({ margin_top: MARGIN/2, margin_bottom: MARGIN/2, margin_left: MARGIN, margin_right: MARGIN }); + 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'); + this.palettes.push([_("New palette"), colors]); + this._savePalettes(); + }, + + _removePalette: function(paletteIndex) { + this.palettes.splice(paletteIndex, 1); + this._savePalettes(); + } +}); + const PrefsPage = new GObject.Class({ Name: 'DrawOnYourScreenPrefsPage', GTypeName: 'DrawOnYourScreenPrefsPage', Extends: Gtk.ScrolledWindow, _init: function(params) { - this.parent(); + this.parent({ hscrollbar_policy: Gtk.PolicyType.NEVER }); let settings = Convenience.getSettings(); let internalShortcutSettings = Convenience.getSettings(Me.metadata['settings-schema'] + '.internal-shortcuts'); @@ -311,19 +518,6 @@ const PrefsPage = new GObject.Class({ 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: _("Default drawing style attributes (color palette, font, line, dash) are defined in an editable css file.\n" + - "See ā€œ%sā€.").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, @@ -346,6 +540,89 @@ const PrefsPage = new GObject.Class({ } }); +const PrefRow = new GObject.Class({ + Name: 'DrawOnYourScreenPrefRow', + GTypeName: 'DrawOnYourScreenPrefRow', + Extends: Gtk.ListBoxRow, + + _init: function(params) { + this.parent({ activatable: false }); + + let hbox = new Gtk.Box({ margin_top: MARGIN/2, margin_bottom: MARGIN/2, margin_left: MARGIN, margin_right: MARGIN }); + 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); + + labelBox.pack_start(new Gtk.Label({ use_markup: true, label: params.label, halign: Gtk.Align.START }), true, true, 0); + if (params.desc) { + let desc = new Gtk.Label({ use_markup: true, label: `${params.desc}`, halign: Gtk.Align.START, wrap: true, xalign: 0 }); + desc.get_style_context().add_class('dim-label'); + labelBox.pack_start(desc, true, true, 0); + this.widgetBox.set_valign(Gtk.Align.START); + } + }, + + addWidget: function(widget) { + this.widgetBox.add(widget); + } +}); + +const PixelSpinButton = new GObject.Class({ + Name: 'DrawOnYourScreenPixelSpinButton', + GTypeName: 'DrawOnYourScreenPixelSpinButton', + Extends: Gtk.SpinButton, + + // Add 'px' unit. + vfunc_output: function() { + this.text = `${Math.round(this.value * 100) / 100} px`; + 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'); + } + } +}); + // this code comes from Sticky Notes View by Sam Bull, https://extensions.gnome.org/extension/568/notes/ const KeybindingsWidget = new GObject.Class({ Name: 'DrawOnYourScreenKeybindings.Widget', diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled index 269a8e8..67d7ed9 100644 Binary files a/schemas/gschemas.compiled and b/schemas/gschemas.compiled differ diff --git a/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml b/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml index 6211353..7ed725d 100644 --- a/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml +++ b/schemas/org.gnome.shell.extensions.draw-on-your-screen.gschema.xml @@ -37,6 +37,7 @@ erase drawing + @@ -229,7 +230,7 @@ switch font family switch font family - + ["<Primary><Shift>f"] switch font family (reverse) switch font family (reverse) @@ -244,6 +245,16 @@ switch font style switch font style + + ["<Primary>KP_Divide","<Primary>slash"] + switch color palette + switch color palette + + + ["<Primary><Shift>KP_Divide","<Primary><Shift>slash"] + switch color palette (reverse) + switch color palette (reverse) + ["<Primary><Shift>a"] switch text alignment @@ -254,11 +265,6 @@ switch image file switch image file - - ["<Primary>o"] - open user stylesheet to edit style - open user stylesheet to edit style - ["<Primary><Shift>s"] Save drawing as a svg file @@ -290,4 +296,75 @@ toggle help + + + "#2e2e2e" + Drawing area background color + The color of the drawing area background + + + true + Automatic dash array + Compute the lengths from the line width + + + 5 + Dash array on + The dash length in pixels + + + 15 + Dash array off + The gap between the dashes in pixels + + + 0 + Dash offset + The dash offset in pixels + + + "Gray" + Grid overlay color + The color of the lines + + + true + Automatic grid overlay line + Compute the lengths from the screen size + + + 10 + Grid overlay line spacing + The gap between lines in pixels + + + 0.5 + Grid overlay line width + The line width in pixels + + + + [ + ("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)"]) + ] + + Color palettes + The palettes of drawing colors + + + true + Automatic square area size + Compute the size of the square area from the screen size + + + 512 + Square area size + The size of the square area in pixels + + diff --git a/stylesheet.css b/stylesheet.css index 8da1267..66e12a6 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -1,7 +1,3 @@ -@import "./data/default.css"; - -/* The following styles don't affect the drawing */ - /* square area */ .draw-on-your-screen-square-area {