add "file-rule" drawing element attribute

This commit is contained in:
abakkk 2020-06-08 20:50:23 +02:00
parent 9bde4d3013
commit 04894a9031
9 changed files with 82 additions and 19 deletions

View File

@ -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;

View File

@ -0,0 +1,3 @@
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<polygon fill="#555" stroke="transparent" fill-rule="evenodd" points="100,10 40,190 190,74 10,74 160,190"/>
</svg>

After

Width:  |  Height:  |  Size: 219 B

View File

@ -0,0 +1,3 @@
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<polygon fill="#555" stroke="transparent" fill-rule="nonzero" points="100,10 40,190 190,74 10,74 160,190"/>
</svg>

After

Width:  |  Height:  |  Size: 219 B

72
draw.js
View File

@ -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);

View File

@ -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),

View File

@ -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 ""

View File

@ -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)",

Binary file not shown.

View File

@ -107,12 +107,12 @@
<description>unselect shape (free drawing)</description>
</key>
<key type="as" name="increment-line-width">
<default>["&lt;Primary&gt;KP_Add"]</default>
<default><![CDATA[['<Primary>KP_Add','<Primary><Shift>plus']]]></default>
<summary>increment the line width</summary>
<description>increment the line width</description>
</key>
<key type="as" name="decrement-line-width">
<default>["&lt;Primary&gt;KP_Subtract"]</default>
<default><![CDATA[['<Primary>KP_Subtract','<Primary>minus','<Primary><Shift>minus']]]></default>
<summary>decrement the line width</summary>
<description>decrement the line width</description>
</key>
@ -136,6 +136,11 @@
<summary>toggle linecap</summary>
<description>toggle linecap</description>
</key>
<key type="as" name="toggle-fill-rule">
<default><![CDATA[['<Primary>KP_Multiply','<Primary>asterisk','<Primary><Shift>asterisk']]]></default>
<summary>toggle fill rule</summary>
<description>toggle fill rule</description>
</key>
<key type="as" name="toggle-dash">
<default>["&lt;Primary&gt;period"]</default>
<summary>toggle dash</summary>