diff --git a/data/default.css b/data/default.css
index 67f91a5..61e858f 100644
--- a/data/default.css
+++ b/data/default.css
@@ -9,6 +9,8 @@
* 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:
* dash-array-on is the length of dashes (no dashes if 0, you can put 0.1 to get dots or square according to line-cap).
@@ -33,6 +35,7 @@
-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;
diff --git a/data/icons/fillrule-evenodd-symbolic.svg b/data/icons/fillrule-evenodd-symbolic.svg
new file mode 100644
index 0000000..a74de4c
--- /dev/null
+++ b/data/icons/fillrule-evenodd-symbolic.svg
@@ -0,0 +1,3 @@
+
diff --git a/data/icons/fillrule-nonzero-symbolic.svg b/data/icons/fillrule-nonzero-symbolic.svg
new file mode 100644
index 0000000..a3b9b2b
--- /dev/null
+++ b/data/icons/fillrule-nonzero-symbolic.svg
@@ -0,0 +1,3 @@
+
diff --git a/draw.js b/draw.js
index 11f4447..445ec11 100644
--- a/draw.js
+++ b/draw.js
@@ -49,18 +49,22 @@ const _ = imports.gettext.domain(Me.metadata['gettext-domain']).gettext;
const GS_VERSION = Config.PACKAGE_VERSION;
-const FILL_ICON_PATH = Me.dir.get_child('data').get_child('icons').get_child('fill-symbolic.svg').get_path();
-const STROKE_ICON_PATH = Me.dir.get_child('data').get_child('icons').get_child('stroke-symbolic.svg').get_path();
-const LINEJOIN_ICON_PATH = Me.dir.get_child('data').get_child('icons').get_child('linejoin-symbolic.svg').get_path();
-const LINECAP_ICON_PATH = Me.dir.get_child('data').get_child('icons').get_child('linecap-symbolic.svg').get_path();
-const DASHED_LINE_ICON_PATH = Me.dir.get_child('data').get_child('icons').get_child('dashed-line-symbolic.svg').get_path();
-const FULL_LINE_ICON_PATH = Me.dir.get_child('data').get_child('icons').get_child('full-line-symbolic.svg').get_path();
+const ICON_DIR = Me.dir.get_child('data').get_child('icons');
+const FILL_ICON_PATH = ICON_DIR.get_child('fill-symbolic.svg').get_path();
+const STROKE_ICON_PATH = ICON_DIR.get_child('stroke-symbolic.svg').get_path();
+const LINEJOIN_ICON_PATH = ICON_DIR.get_child('linejoin-symbolic.svg').get_path();
+const LINECAP_ICON_PATH = ICON_DIR.get_child('linecap-symbolic.svg').get_path();
+const FILLRULE_NONZERO_ICON_PATH = ICON_DIR.get_child('fillrule-nonzero-symbolic.svg').get_path();
+const FILLRULE_EVENODD_ICON_PATH = ICON_DIR.get_child('fillrule-evenodd-symbolic.svg').get_path();
+const DASHED_LINE_ICON_PATH = ICON_DIR.get_child('dashed-line-symbolic.svg').get_path();
+const FULL_LINE_ICON_PATH = ICON_DIR.get_child('full-line-symbolic.svg').get_path();
var Shapes = { NONE: 0, LINE: 1, ELLIPSE: 2, RECTANGLE: 3, TEXT: 4, POLYGON: 5, POLYLINE: 6 };
const TextState = { DRAWING: 0, WRITING: 1 };
const ShapeNames = { 0: "Free drawing", 1: "Line", 2: "Ellipse", 3: "Rectangle", 4: "Text", 5: "Polygon", 6: "Polyline" };
const LineCapNames = { 0: 'Butt', 1: 'Round', 2: 'Square' };
const LineJoinNames = { 0: 'Miter', 1: 'Round', 2: 'Bevel' };
+const FillRuleNames = { 0: 'Nonzero', 1: 'Evenodd' };
const FontWeightNames = { 0: 'Normal', 1: 'Bold' };
const FontStyleNames = { 0: 'Normal', 1: 'Italic', 2: 'Oblique' };
const FontFamilyNames = { 0: 'Default', 1: 'Sans-Serif', 2: 'Serif', 3: 'Monospace', 4: 'Cursive', 5: 'Fantasy' };
@@ -155,6 +159,7 @@ var DrawingArea = new Lang.Class({
this.currentLineWidth = themeNode.get_length('-drawing-line-width');
this.currentLineJoin = themeNode.get_double('-drawing-line-join');
this.currentLineCap = themeNode.get_double('-drawing-line-cap');
+ this.currentFillRule = themeNode.get_double('-drawing-fill-rule');
this.dashArray = [themeNode.get_length('-drawing-dash-array-on'), themeNode.get_length('-drawing-dash-array-off')];
this.dashOffset = themeNode.get_length('-drawing-dash-offset');
let font = themeNode.get_font();
@@ -179,6 +184,7 @@ var DrawingArea = new Lang.Class({
this.currentLineWidth = (this.currentLineWidth > 0) ? this.currentLineWidth : 3;
this.currentLineJoin = ([0, 1, 2].indexOf(this.currentLineJoin) != -1) ? this.currentLineJoin : Cairo.LineJoin.ROUND;
this.currentLineCap = ([0, 1, 2].indexOf(this.currentLineCap) != -1) ? this.currentLineCap : Cairo.LineCap.ROUND;
+ this.currentFillRule = ([0, 1].indexOf(this.currentFillRule) != -1) ? this.currentFillRule : Cairo.FillRule.WINDING;
this.currentFontFamilyId = 0;
this.currentFontWeight = this.currentFontWeight > 500 ? 1 : 0 ;
// font style enum order of Cairo and Pango are different
@@ -384,6 +390,7 @@ var DrawingArea = new Lang.Class({
line: { lineWidth: this.currentLineWidth, lineJoin: this.currentLineJoin, lineCap: this.currentLineCap },
dash: { array: this.dashedLine ? this.dashArray : [0, 0] , offset: this.dashedLine ? this.dashOffset : 0 },
fill: this.fill,
+ fillRule: this.currentFillRule,
eraser: eraser,
transform: { active: false, center: [0, 0], angle: 0, startAngle: 0, ratio: 1 },
text: '',
@@ -647,6 +654,11 @@ var DrawingArea = new Lang.Class({
this.emit('show-osd', null, _(LineCapNames[this.currentLineCap]), "", -1);
},
+ toggleFillRule: function() {
+ this.currentFillRule = this.currentFillRule == 1 ? 0 : this.currentFillRule + 1;
+ this.emit('show-osd', null, _(FillRuleNames[this.currentFillRule]), "", -1);
+ },
+
toggleFontWeight: function() {
this.currentFontWeight = this.currentFontWeight == 1 ? 0 : this.currentFontWeight + 1;
if (this.currentElement) {
@@ -920,6 +932,10 @@ const DrawingElement = new Lang.Class({
_init: function(params) {
for (let key in params)
this[key] = params[key];
+
+ // compatibility with json generated by old extension versions
+ if (params.fillRule === undefined)
+ this.fillRule = Cairo.FillRule.WINDING;
},
// toJSON is called by JSON.stringify
@@ -930,6 +946,7 @@ const DrawingElement = new Lang.Class({
line: this.line,
dash: this.dash,
fill: this.fill,
+ fillRule: this.fillRule,
eraser: this.eraser,
transform: this.transform,
text: this.text,
@@ -942,6 +959,7 @@ const DrawingElement = new Lang.Class({
cr.setLineCap(this.line.lineCap);
cr.setLineJoin(this.line.lineJoin);
cr.setLineWidth(this.line.lineWidth);
+ cr.setFillRule(this.fillRule);
if (this.dash.array[0] > 0 && this.dash.array[1] > 0)
cr.setDash(this.dash.array, this.dash.offset);
@@ -1019,8 +1037,8 @@ const DrawingElement = new Lang.Class({
`stroke-linecap="${LineCapNames[this.line.lineCap].toLowerCase()}"`;
else
attributes = `fill="${fill ? color : 'transparent'}" ` +
+ (fill ? `fill-rule="${FillRuleNames[this.fillRule].toLowerCase()}" ` : `fill-opacity="0" `) +
`stroke="${color}" ` +
- `${fill ? '' : 'fill-opacity="0"'} ` +
`stroke-width="${this.line.lineWidth}" ` +
`stroke-linecap="${LineCapNames[this.line.lineCap].toLowerCase()}" ` +
`stroke-linejoin="${LineJoinNames[this.line.lineJoin].toLowerCase()}"`;
@@ -1404,6 +1422,8 @@ const DrawingMenu = new Lang.Class({
this.strokeIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(STROKE_ICON_PATH) });
this.fillIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FILL_ICON_PATH) });
+ this.fillRuleNonzeroIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FILLRULE_NONZERO_ICON_PATH) });
+ this.fillRuleEvenoddIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FILLRULE_EVENODD_ICON_PATH) });
this.linejoinIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(LINEJOIN_ICON_PATH) });
this.linecapIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(LINECAP_ICON_PATH) });
this.fullLineIcon = new Gio.FileIcon({ file: Gio.File.new_for_path(FULL_LINE_ICON_PATH) });
@@ -1463,9 +1483,12 @@ const DrawingMenu = new Lang.Class({
this.menu.addAction(_("Smooth"), this.area.smoothLastElement.bind(this.area), 'format-text-strikethrough-symbolic');
this._addSeparator(this.menu);
- this._addSubMenuItem(this.menu, null, ShapeNames, this.area, 'currentShape', this.updateSectionVisibility.bind(this));
+ this._addSubMenuItem(this.menu, null, ShapeNames, this.area, 'currentShape', this._updateSectionVisibility.bind(this));
this._addColorSubMenuItem(this.menu);
- this.fillItem = this._addSwitchItem(this.menu, _("Fill"), this.strokeIcon, this.fillIcon, this.area, 'fill');
+ this.fillItem = this._addSwitchItem(this.menu, _("Fill"), this.strokeIcon, this.fillIcon, this.area, 'fill', this._updateSectionVisibility.bind(this));
+ this.fillSection = new PopupMenu.PopupMenuSection();
+ this._addSubMenuItem(this.fillSection, this.fillRuleNonzeroIcon, FillRuleNames, this.area, 'currentFillRule', this._updateFillRuleIcon.bind(this));
+ this.menu.addMenuItem(this.fillSection);
this._addSeparator(this.menu);
let lineSection = new PopupMenu.PopupMenuSection();
@@ -1489,10 +1512,10 @@ const DrawingMenu = new Lang.Class({
this.fontSection = fontSection;
let manager = Extension.manager;
- this._addSwitchItemWithCallback(this.menu, _("Hide panel and dock"), manager.hiddenList ? true : false, manager.togglePanelAndDockOpacity.bind(manager));
- this._addSwitchItemWithCallback(this.menu, _("Add a drawing background"), this.area.hasBackground, this.area.toggleBackground.bind(this.area));
- this._addSwitchItemWithCallback(this.menu, _("Add a grid overlay"), this.area.hasGrid, this.area.toggleGrid.bind(this.area));
- this._addSwitchItemWithCallback(this.menu, _("Square drawing area"), this.area.isSquareArea, this.area.toggleSquareArea.bind(this.area));
+ this._addSimpleSwitchItem(this.menu, _("Hide panel and dock"), manager.hiddenList ? true : false, manager.togglePanelAndDockOpacity.bind(manager));
+ this._addSimpleSwitchItem(this.menu, _("Add a drawing background"), this.area.hasBackground, this.area.toggleBackground.bind(this.area));
+ this._addSimpleSwitchItem(this.menu, _("Add a grid overlay"), this.area.hasGrid, this.area.toggleGrid.bind(this.area));
+ this._addSimpleSwitchItem(this.menu, _("Square drawing area"), this.area.isSquareArea, this.area.toggleSquareArea.bind(this.area));
this._addSeparator(this.menu);
this._addDrawingNameItem(this.menu);
@@ -1503,22 +1526,35 @@ const DrawingMenu = new Lang.Class({
this.menu.addAction(_("Edit style"), manager.openUserStyleFile.bind(manager), 'document-page-setup-symbolic');
this.menu.addAction(_("Show help"), () => { this.close(); this.area.toggleHelp(); }, 'preferences-desktop-keyboard-shortcuts-symbolic');
- this.updateSectionVisibility();
+ this._updateSectionVisibility();
+ this._updateFillRuleIcon();
},
- updateSectionVisibility: function() {
+ _updateSectionVisibility: function() {
if (this.area.currentShape != Shapes.TEXT) {
this.lineSection.actor.show();
this.fontSection.actor.hide();
this.fillItem.setSensitive(true);
+ this.fillSection.setSensitive(true);
} else {
this.lineSection.actor.hide();
this.fontSection.actor.show();
this.fillItem.setSensitive(false);
+ this.fillSection.setSensitive(false);
}
+
+ if (this.area.fill)
+ this.fillSection.actor.show();
+ else
+ this.fillSection.actor.hide();
},
- _addSwitchItem: function(menu, label, iconFalse, iconTrue, target, targetProperty) {
+ _updateFillRuleIcon: function() {
+ let fillRuleIcon = this.area.currentFillRule == Cairo.FillRule.EVEN_ODD ? this.fillRuleEvenoddIcon : this.fillRuleNonzeroIcon;
+ this.fillSection.firstMenuItem.icon.set_gicon(fillRuleIcon);
+ },
+
+ _addSwitchItem: function(menu, label, iconFalse, iconTrue, target, targetProperty, onToggled) {
let item = new PopupMenu.PopupSwitchMenuItem(label, target[targetProperty]);
item.icon = new St.Icon({ style_class: 'popup-menu-icon' });
@@ -1528,12 +1564,14 @@ const DrawingMenu = new Lang.Class({
item.connect('toggled', (item, state) => {
target[targetProperty] = state;
item.icon.set_gicon(target[targetProperty] ? iconTrue : iconFalse);
+ if (onToggled)
+ onToggled();
});
menu.addMenuItem(item);
return item;
},
- _addSwitchItemWithCallback: function(menu, label, active, onToggled) {
+ _addSimpleSwitchItem: function(menu, label, active, onToggled) {
let item = new PopupMenu.PopupSwitchMenuItem(label, active);
item.connect('toggled', onToggled);
menu.addMenuItem(item);
diff --git a/extension.js b/extension.js
index 9a9afce..17bbb08 100644
--- a/extension.js
+++ b/extension.js
@@ -184,6 +184,7 @@ var AreaManager = new Lang.Class({
'decrement-line-width-more': () => this.activeArea.incrementLineWidth(-5),
'toggle-linejoin': this.activeArea.toggleLineJoin.bind(this.activeArea),
'toggle-linecap': this.activeArea.toggleLineCap.bind(this.activeArea),
+ 'toggle-fill-rule': this.activeArea.toggleFillRule.bind(this.activeArea),
'toggle-dash' : this.activeArea.toggleDash.bind(this.activeArea),
'toggle-fill' : this.activeArea.toggleFill.bind(this.activeArea),
'select-none-shape': () => this.activeArea.selectShape(Draw.Shapes.NONE),
diff --git a/locale/draw-on-your-screen.pot b/locale/draw-on-your-screen.pot
index 688baa7..c221e6d 100644
--- a/locale/draw-on-your-screen.pot
+++ b/locale/draw-on-your-screen.pot
@@ -200,6 +200,9 @@ msgstr ""
msgid "Change linecap"
msgstr ""
+msgid "Change fill rule"
+msgstr ""
+
#: already in draw.js
#:msgid "Dashed line"
#:msgstr ""
@@ -372,6 +375,12 @@ msgstr ""
#msgid "Bevel"
#msgstr ""
+#msgid "Nonzero"
+#msgstr ""
+
+#msgid "Evenodd"
+#msgstr ""
+
#msgid "Normal"
#msgstr ""
diff --git a/prefs.js b/prefs.js
index 4e958f0..9c7aaad 100644
--- a/prefs.js
+++ b/prefs.js
@@ -60,6 +60,7 @@ var INTERNAL_KEYBINDINGS = {
'decrement-line-width-more': "Decrement line width even more",
'toggle-linejoin': "Change linejoin",
'toggle-linecap': "Change linecap",
+ 'toggle-fill-rule': "Change fill rule",
'toggle-dash': "Dashed line",
'-separator-3': '',
'toggle-font-family': "Change font family (generic name)",
diff --git a/schemas/gschemas.compiled b/schemas/gschemas.compiled
index b90b102..ce20632 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 7a3fab7..3c9cd0f 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
@@ -107,12 +107,12 @@
unselect shape (free drawing)
- ["<Primary>KP_Add"]
+ KP_Add','plus']]]>
increment the line width
increment the line width
- ["<Primary>KP_Subtract"]
+ KP_Subtract','minus','minus']]]>
decrement the line width
decrement the line width
@@ -136,6 +136,11 @@
toggle linecap
toggle linecap
+
+ KP_Multiply','asterisk','asterisk']]]>
+ toggle fill rule
+ toggle fill rule
+
["<Primary>period"]
toggle dash