Compare commits
	
		
			2 Commits
		
	
	
		
			3.22.3
			...
			wip/pad-os
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | f9693d3a2b | ||
|   | dc280ef2ad | 
| @@ -35,6 +35,7 @@ endif | ||||
|  | ||||
| introspectiondir = $(datadir)/dbus-1/interfaces | ||||
| introspection_DATA =				\ | ||||
| 	org.gnome.Shell.PadOsd.xml		\ | ||||
| 	org.gnome.Shell.Screencast.xml		\ | ||||
| 	org.gnome.Shell.Screenshot.xml		\ | ||||
| 	org.gnome.ShellSearchProvider.xml	\ | ||||
| @@ -114,6 +115,7 @@ EXTRA_DIST =						\ | ||||
| 	$(convert_DATA)					\ | ||||
| 	$(keys_in_files)				\ | ||||
| 	$(dist_theme_files)				\ | ||||
| 	pad-osd.css					\ | ||||
| 	perf-background.xml.in				\ | ||||
| 	org.gnome.Shell.PortalHelper.desktop.in		\ | ||||
| 	org.gnome.Shell.PortalHelper.service.in		\ | ||||
|   | ||||
| @@ -22,6 +22,7 @@ | ||||
|     <file>no-events.svg</file> | ||||
|     <file>no-notifications.svg</file> | ||||
|     <file>noise-texture.png</file> | ||||
|     <file>pad-osd.css</file> | ||||
|     <file>page-indicator-active.svg</file> | ||||
|     <file>page-indicator-inactive.svg</file> | ||||
|     <file>page-indicator-checked.svg</file> | ||||
|   | ||||
							
								
								
									
										28
									
								
								data/org.gnome.Shell.PadOsd.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								data/org.gnome.Shell.PadOsd.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <!DOCTYPE node PUBLIC | ||||
| '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN' | ||||
| 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'> | ||||
| <node> | ||||
|  | ||||
|   <!-- | ||||
|       org.gnome.Shell.PadOSD: | ||||
|       @short_description: Pad OSD interface | ||||
|  | ||||
|       The interface used to show button map OSD on pad devices. | ||||
|   --> | ||||
|   <interface name='org.gnome.Shell.Wacom.PadOsd'> | ||||
|  | ||||
|     <!-- | ||||
|         Show: | ||||
|         @device_node: device node file, usually in /dev/input/... | ||||
|         @edition_mode: whether toggling edition mode on when showing | ||||
|  | ||||
|         Shows the pad button map OSD for the requested device, the OSD | ||||
| 	will be shown according the current device settings (output | ||||
| 	mapping, left handed mode, ...) | ||||
|     --> | ||||
|     <method name='Show'> | ||||
|       <arg name='device_node' direction='in' type='o'/> | ||||
|       <arg name='edition_mode' direction='in' type='b'/> | ||||
|     </method> | ||||
|   </interface> | ||||
| </node> | ||||
| @@ -562,6 +562,10 @@ StScrollBar { | ||||
|     background-color: #eeeeec; | ||||
|     border-radius: 0.3em; } | ||||
|  | ||||
| .pad-osd-window { | ||||
|     background-color: rgba(0, 0, 0, 0.8); | ||||
| } | ||||
|  | ||||
| /* App Switcher */ | ||||
| .switcher-popup { | ||||
|   padding: 8px; | ||||
|   | ||||
							
								
								
									
										30
									
								
								data/theme/pad-osd.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								data/theme/pad-osd.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| .Leader { | ||||
|     stroke-width: .5 !important; | ||||
|     stroke: #535353; | ||||
|     fill: none !important; | ||||
| } | ||||
|  | ||||
| .Button { | ||||
|     stroke-width: .25; | ||||
|     stroke: #ededed; | ||||
|     fill: #ededed; | ||||
| } | ||||
|  | ||||
| .Ring { | ||||
|     stroke-width: .5 !important; | ||||
|     stroke: #535353 !important; | ||||
|     fill: none !important; | ||||
| } | ||||
|  | ||||
| .Label { | ||||
|     stroke: none !important; | ||||
|     stroke-width: .1 !important; | ||||
|     font-size: .1 !important; | ||||
|     fill: transparent !important; | ||||
| } | ||||
|  | ||||
| .TouchStrip, .TouchRing { | ||||
|     stroke-width: .1 !important; | ||||
|     stroke: #ededed !important; | ||||
|     fill: #535353 !important; | ||||
| } | ||||
| @@ -72,6 +72,7 @@ | ||||
|     <file>ui/osdMonitorLabeler.js</file> | ||||
|     <file>ui/overview.js</file> | ||||
|     <file>ui/overviewControls.js</file> | ||||
|     <file>ui/padOsd.js</file> | ||||
|     <file>ui/panel.js</file> | ||||
|     <file>ui/panelMenu.js</file> | ||||
|     <file>ui/pointerWatcher.js</file> | ||||
|   | ||||
| @@ -26,6 +26,7 @@ const ModalDialog = imports.ui.modalDialog; | ||||
| const OsdWindow = imports.ui.osdWindow; | ||||
| const OsdMonitorLabeler = imports.ui.osdMonitorLabeler; | ||||
| const Overview = imports.ui.overview; | ||||
| const PadOsd = imports.ui.padOsd; | ||||
| const Panel = imports.ui.panel; | ||||
| const Params = imports.misc.params; | ||||
| const RunDialog = imports.ui.runDialog; | ||||
| @@ -61,6 +62,7 @@ let screenShield = null; | ||||
| let notificationDaemon = null; | ||||
| let windowAttentionHandler = null; | ||||
| let ctrlAltTabManager = null; | ||||
| let padOsdService = null; | ||||
| let osdWindowManager = null; | ||||
| let osdMonitorLabeler = null; | ||||
| let sessionMode = null; | ||||
| @@ -155,6 +157,7 @@ function _initializeUI() { | ||||
|     // working until it's updated. | ||||
|     uiGroup = layoutManager.uiGroup; | ||||
|  | ||||
|     padOsdService = new PadOsd.PadOsdService(); | ||||
|     screencastService = new Screencast.ScreencastService(); | ||||
|     xdndHandler = new XdndHandler.XdndHandler(); | ||||
|     ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); | ||||
|   | ||||
							
								
								
									
										733
									
								
								js/ui/padOsd.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										733
									
								
								js/ui/padOsd.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,733 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const St = imports.gi.St; | ||||
| const Rsvg = imports.gi.Rsvg; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GDesktopEnums = imports.gi.GDesktopEnums; | ||||
| const Atk = imports.gi.Atk; | ||||
| const Cairo = imports.cairo; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const Layout = imports.ui.layout; | ||||
|  | ||||
| const ACTIVE_COLOR = "#729fcf"; | ||||
|  | ||||
| const LTR = 0; | ||||
| const RTL = 1; | ||||
|  | ||||
| const CW = 0; | ||||
| const CCW = 1; | ||||
|  | ||||
| const UP = 0; | ||||
| const DOWN = 1; | ||||
|  | ||||
| const KeybindingEntry = new Lang.Class({ | ||||
|     Name: 'KeybindingEntry', | ||||
|  | ||||
|     _init: function () { | ||||
|         this.actor = new St.Entry({ hint_text: _('New shortcut...'), | ||||
|                                     width: 120 }); | ||||
|         this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this.destroy)); | ||||
|     }, | ||||
|  | ||||
|     _onCapturedEvent: function (actor, event) { | ||||
|         if (event.type() == Clutter.EventType.KEY_PRESS) { | ||||
|             if (GLib.unichar_isprint(event.get_key_unicode())) { | ||||
|                 let str = Gtk.accelerator_name_with_keycode(null, | ||||
|                                                             event.get_key_symbol(), | ||||
|                                                             event.get_key_code(), | ||||
|                                                             event.get_state()); | ||||
|                 this.actor.set_text(str); | ||||
|                 this.emit('keybinding', str); | ||||
|             } | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     destroy: function () { | ||||
|         this.actor.destroy(); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(KeybindingEntry.prototype); | ||||
|  | ||||
| const ActionComboBox = new Lang.Class({ | ||||
|     Name: 'ActionComboBox', | ||||
|  | ||||
|     _init: function () { | ||||
|         this.actor = new St.Button({ style_class: 'button' }); | ||||
|         this.actor.connect('clicked', Lang.bind(this, this._onButtonClicked)); | ||||
|         this.actor.set_toggle_mode(true); | ||||
|  | ||||
|         let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL, | ||||
|                                                 spacing: 6 }); | ||||
|         let box = new St.Widget({ layout_manager: boxLayout }); | ||||
|         this.actor.set_child(box); | ||||
|  | ||||
|         this._label = new St.Label({ width: 150 }); | ||||
|         box.add_child(this._label) | ||||
|  | ||||
|         let arrow = new St.Icon({ style_class: 'popup-menu-arrow', | ||||
|                                   icon_name: 'pan-down-symbolic', | ||||
|                                   accessible_role: Atk.Role.ARROW, | ||||
|                                   y_expand: true, | ||||
|                                   y_align: Clutter.ActorAlign.CENTER }); | ||||
|         box.add_child(arrow); | ||||
|  | ||||
|         /* Order matches GDesktopPadButtonAction enum */ | ||||
|         this._actions = [_('Application defined'), | ||||
|                          _('Show on-screen help'), | ||||
|                          _('Switch monitor'), | ||||
|                          _('Assign keystroke')]; | ||||
|  | ||||
|         this._editMenu = new PopupMenu.PopupMenu(this.actor, 0, St.Side.TOP); | ||||
|         this._editMenu.connect('menu-closed', Lang.bind(this, function() { this.actor.set_checked(false); })); | ||||
|         this._editMenu.actor.hide(); | ||||
|         Main.uiGroup.add_actor(this._editMenu.actor); | ||||
|  | ||||
|         for (let i = 0; i < this._actions.length; i++) { | ||||
|             let str = this._actions[i]; | ||||
|             let action = i; | ||||
|             this._editMenu.addAction(str, Lang.bind(this, function() { this._onActionSelected(action) })); | ||||
|         } | ||||
|  | ||||
|         this.setAction(GDesktopEnums.PadButtonAction.NONE); | ||||
|     }, | ||||
|  | ||||
|     _onActionSelected: function (action) { | ||||
|         this.setAction(action); | ||||
|         this.popdown(); | ||||
|         this.emit('action', action); | ||||
|     }, | ||||
|  | ||||
|     setAction: function (action) { | ||||
|         this._label.set_text(this._actions[action]); | ||||
|     }, | ||||
|  | ||||
|     popup: function () { | ||||
|         this._editMenu.open(true); | ||||
|     }, | ||||
|  | ||||
|     popdown: function () { | ||||
|         this._editMenu.close(true); | ||||
|     }, | ||||
|  | ||||
|     _onButtonClicked: function () { | ||||
|         if (this.actor.get_checked()) | ||||
|             this.popup(); | ||||
|         else | ||||
|             this.popdown(); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ActionComboBox.prototype); | ||||
|  | ||||
| const ActionEditor = new Lang.Class({ | ||||
|     Name: 'ActionEditor', | ||||
|  | ||||
|     _init: function () { | ||||
|         let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL, | ||||
|                                                 spacing: 12 }); | ||||
|  | ||||
|         this.actor = new St.Widget({ layout_manager: boxLayout }); | ||||
|  | ||||
|         this._actionComboBox = new ActionComboBox(); | ||||
|         this._actionComboBox.connect('action', Lang.bind(this, this._onActionSelected)); | ||||
|         this.actor.add_actor(this._actionComboBox.actor); | ||||
|  | ||||
|         this._keybindingEdit = new KeybindingEntry(); | ||||
|         this._keybindingEdit.connect('keybinding', Lang.bind(this, this._onKeybindingEdited)); | ||||
|         this._keybindingEdit.actor.hide(); | ||||
|         this.actor.add_actor(this._keybindingEdit.actor); | ||||
|  | ||||
|         this._doneButton = new St.Button ({ label: _('Done'), | ||||
|                                             width: 100, | ||||
|                                             style_class: 'button'}); | ||||
|         this._doneButton.connect('clicked', Lang.bind(this, this._onEditingDone)); | ||||
|         this.actor.add_actor(this._doneButton); | ||||
|     }, | ||||
|  | ||||
|     setSettings: function (settings) { | ||||
|         this._buttonSettings = settings; | ||||
|  | ||||
|         this._currentAction = this._buttonSettings.get_enum('action'); | ||||
|         this._currentKeybinding = this._buttonSettings.get_string('keybinding'); | ||||
|         this._actionComboBox.setAction (this._currentAction); | ||||
|  | ||||
|         if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING) { | ||||
|             this._keybindingEdit.actor.set_text(this._currentKeybinding); | ||||
|             this._keybindingEdit.actor.show(); | ||||
|         } else { | ||||
|             this._keybindingEdit.actor.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     close: function() { | ||||
|         this._actionComboBox.popdown(); | ||||
|         this.actor.hide(); | ||||
|     }, | ||||
|  | ||||
|     _onKeybindingEdited: function (entry, keybinding) { | ||||
|         this._currentKeybinding = keybinding; | ||||
|     }, | ||||
|  | ||||
|     _onActionSelected: function (menu, action) { | ||||
|         this._currentAction = action; | ||||
|  | ||||
|         if (action == GDesktopEnums.PadButtonAction.KEYBINDING) { | ||||
|             this._keybindingEdit.actor.show(); | ||||
|             this._keybindingEdit.actor.grab_key_focus(); | ||||
|         } else { | ||||
|             this._keybindingEdit.actor.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _storeSettings: function () { | ||||
|         if (!this._buttonSettings) | ||||
|             return; | ||||
|  | ||||
|         let keybinding = null; | ||||
|  | ||||
|         if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING) | ||||
|             keybinding = this._currentKeybinding; | ||||
|  | ||||
|         this._buttonSettings.set_enum('action', this._currentAction); | ||||
|  | ||||
|         if (keybinding) | ||||
|             this._buttonSettings.set_string('keybinding', keybinding); | ||||
|         else | ||||
|             this._buttonSettings.reset('keybinding'); | ||||
|     }, | ||||
|  | ||||
|     _onEditingDone: function () { | ||||
|         this._storeSettings(); | ||||
|         this.close(); | ||||
|         this.emit ('done'); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(ActionEditor.prototype); | ||||
|  | ||||
| const PadDiagram = new Lang.Class({ | ||||
|     Name: 'PadDiagram', | ||||
|     Extends: St.DrawingArea, | ||||
|  | ||||
|     _init: function (imagePath, leftHanded) { | ||||
|         this.parent(); | ||||
|  | ||||
|         let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/pad-osd.css'); | ||||
|         let [success, css, etag] = file.load_contents(null); | ||||
|         this._css = css; | ||||
|  | ||||
|         let originalHandle = Rsvg.Handle.new_from_file(imagePath); | ||||
|         let dimensions = originalHandle.get_dimensions(); | ||||
|         this._imageWidth = dimensions.width; | ||||
|         this._imageHeight = dimensions.height; | ||||
|  | ||||
|         this._activeButtons = []; | ||||
|         this._imagePath = imagePath; | ||||
|         this._handle = this._composeStyledDiagram(); | ||||
|         this.connect('repaint', Lang.bind(this, this._repaint)); | ||||
|         this.connect('notify::size', Lang.bind(this, this._updateScale)); | ||||
|         this._leftHanded = leftHanded; | ||||
|     }, | ||||
|  | ||||
|     _wrappingSvgHeader: function () { | ||||
|         return ('<?xml version="1.0" encoding="UTF-8" standalone="no"?>' + | ||||
|                 '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" ' + | ||||
|                 'xmlns:xi="http://www.w3.org/2001/XInclude" ' + | ||||
|                 'width="' + this._imageWidth + '" height="' + this._imageHeight + '"> ' + | ||||
|                 '<style type="text/css">'); | ||||
|     }, | ||||
|  | ||||
|     _wrappingSvgFooter: function () { | ||||
|         return ('</style>' + | ||||
|                 '<xi:include href="' + this._imagePath + '" />' + | ||||
|                 '</svg>'); | ||||
|     }, | ||||
|  | ||||
|     _cssString: function () { | ||||
|         let css = this._css; | ||||
|  | ||||
|         for (let i = 0; i < this._activeButtons.length; i++) { | ||||
|             let ch = String.fromCharCode('A'.charCodeAt() + this._activeButtons[i]); | ||||
|             css += ('.' + ch + ' { ' + | ||||
| 	            '  stroke: ' + ACTIVE_COLOR + ' !important; ' + | ||||
|                     '  fill: ' + ACTIVE_COLOR + ' !important; ' + | ||||
|                     '} '); | ||||
|         } | ||||
|  | ||||
|         return css; | ||||
|     }, | ||||
|  | ||||
|     _composeStyledDiagram: function () { | ||||
|         let svgData = ''; | ||||
|  | ||||
|         if (!GLib.file_test(this._imagePath, GLib.FileTest.EXISTS)) | ||||
|             return null; | ||||
|  | ||||
|         svgData += this._wrappingSvgHeader(); | ||||
|         svgData += this._cssString(); | ||||
|         svgData += this._wrappingSvgFooter(); | ||||
|  | ||||
|         let handle = new Rsvg.Handle(); | ||||
|         handle.set_base_uri (GLib.path_get_dirname (this._imagePath)); | ||||
|         handle.write(svgData); | ||||
|         handle.close(); | ||||
|  | ||||
|         return handle; | ||||
|     }, | ||||
|  | ||||
|     _updateScale: function () { | ||||
|         let [width, height] = this.get_size(); | ||||
|         let dimensions = this._handle.get_dimensions (); | ||||
|         let scaleX = width / dimensions.width; | ||||
|         let scaleY = height / dimensions.height; | ||||
|         this._scale = Math.min(scaleX, scaleY); | ||||
|     }, | ||||
|  | ||||
|     _repaint: function (area) { | ||||
|         if (this._handle == null) | ||||
|             return; | ||||
|  | ||||
|         let [width, height] = area.get_surface_size(); | ||||
|         let dimensions = this._handle.get_dimensions (); | ||||
|         let cr = this.get_context(); | ||||
|  | ||||
|         if (this._scale == null) | ||||
|             this._updateScale(); | ||||
|  | ||||
|         cr.save(); | ||||
|         cr.translate (width/2, height/2); | ||||
|         cr.scale (this._scale, this._scale); | ||||
|         if (this._leftHanded) | ||||
|             cr.rotate(Math.PI); | ||||
|         cr.translate (-dimensions.width/2, -dimensions.height/2); | ||||
|         this._handle.render_cairo(cr); | ||||
|         cr.restore(); | ||||
|         cr.$dispose(); | ||||
|     }, | ||||
|  | ||||
|     _transformPoint: function (x, y) { | ||||
|         if (this._handle == null || this._scale == null) | ||||
|             return [x, y]; | ||||
|  | ||||
|         // I miss Cairo.Matrix | ||||
|         let [width, height] = this.get_size(); | ||||
|         let dimensions = this._handle.get_dimensions (); | ||||
|         x = x * this._scale + width / 2 - dimensions.width / 2 * this._scale; | ||||
|         y = y * this._scale + height / 2 - dimensions.height / 2 * this._scale;; | ||||
|         return [Math.round(x), Math.round(y)]; | ||||
|     }, | ||||
|  | ||||
|     _getItemLabelCoords: function (labelName, leaderName) { | ||||
|         if (this._handle == null) | ||||
|             return [false]; | ||||
|  | ||||
|         let leaderPos, leaderSize, pos; | ||||
|         let found, direction; | ||||
|  | ||||
|         [found, pos] = this._handle.get_position_sub('#' + labelName); | ||||
|         if (!found) | ||||
|             return [false]; | ||||
|  | ||||
|         [found, leaderPos] = this._handle.get_position_sub('#' + leaderName); | ||||
|         [found, leaderSize] = this._handle.get_dimensions_sub('#' + leaderName); | ||||
|         if (!found) | ||||
|             return [false]; | ||||
|  | ||||
|         if (pos.x > leaderPos.x + leaderSize.width) | ||||
|             direction = LTR; | ||||
|         else | ||||
|             direction = RTL; | ||||
|  | ||||
|         if (this._leftHanded) { | ||||
|             direction = 1 - direction; | ||||
|             pos.x = this._imageWidth - pos.x; | ||||
|             pos.y = this._imageHeight - pos.y; | ||||
|         } | ||||
|  | ||||
|         let [x, y] = this._transformPoint(pos.x, pos.y) | ||||
|  | ||||
|         return [true, x, y, direction]; | ||||
|     }, | ||||
|  | ||||
|     getButtonLabelCoords: function (button) { | ||||
|         let ch = String.fromCharCode('A'.charCodeAt() + button); | ||||
|         let labelName = 'Label' + ch; | ||||
|         let leaderName = 'Leader' + ch; | ||||
|  | ||||
|         return this._getItemLabelCoords(labelName, leaderName); | ||||
|     }, | ||||
|  | ||||
|     getRingLabelCoords: function (number, dir) { | ||||
|         let numStr = number > 0 ? number.toString() : ''; | ||||
|         let dirStr = dir == CW ? 'CW' : 'CCW'; | ||||
|         let labelName = 'LabelRing' + numStr + dirStr; | ||||
|         let leaderName = 'LeaderRing' + numStr + dirStr; | ||||
|  | ||||
|         return this._getItemLabelCoords(labelName, leaderName); | ||||
|     }, | ||||
|  | ||||
|     getStripLabelCoords: function (number, dir) { | ||||
|         let numStr = number > 0 ? (number + 1).toString() : ''; | ||||
|         let dirStr = dir == UP ? 'Up' : 'Down'; | ||||
|         let labelName = 'LabelStrip' + numStr + dirStr; | ||||
|         let leaderName = 'LeaderStrip' + numStr + dirStr; | ||||
|  | ||||
|         return this._getItemLabelCoords(labelName, leaderName); | ||||
|     }, | ||||
|  | ||||
|     _invalidateSvg: function () { | ||||
|         if (this._handle == null) | ||||
|             return; | ||||
|         this._handle = this._composeStyledDiagram(); | ||||
|         this.queue_repaint(); | ||||
|     }, | ||||
|  | ||||
|     activateButton: function (button) { | ||||
|         this._activeButtons.push(button); | ||||
|         this._invalidateSvg (); | ||||
|     }, | ||||
|  | ||||
|     deactivateButton: function (button) { | ||||
|         for (let i = 0; i < this._activeButtons.length; i++) { | ||||
|             if (this._activeButtons[i] == button) | ||||
|                 this._activeButtons.splice(i, 1); | ||||
|         } | ||||
|         this._invalidateSvg (); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const PadOsd = new Lang.Class({ | ||||
|     Name: 'PadOsd', | ||||
|  | ||||
|     _init: function (padDevice, settings, imagePath, editionMode, monitorIndex) { | ||||
|         this.padDevice = padDevice; | ||||
|         this._settings = settings; | ||||
|         this._imagePath = imagePath; | ||||
|         this._editionMode = editionMode; | ||||
|         this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); | ||||
|  | ||||
|         this.actor = new Shell.GenericContainer({ style_class: 'pad-osd-window', | ||||
|                                                    reactive: true, | ||||
|                                                    x: 0, | ||||
|                                                    y: 0, | ||||
|                                                    width: global.screen_width, | ||||
|                                                    height: global.screen_height }); | ||||
|         this.actor.connect('allocate', Lang.bind(this, this._allocate)); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this.destroy)); | ||||
|         Main.uiGroup.add_actor(this.actor); | ||||
|  | ||||
|         this._monitorIndex = monitorIndex; | ||||
|         let constraint = new Layout.MonitorConstraint({ index: monitorIndex }); | ||||
|         this.actor.add_constraint(constraint); | ||||
|  | ||||
|         this._padDiagram = new PadDiagram(this._imagePath, settings.get_boolean('left-handed')); | ||||
|         this.actor.add_actor(this._padDiagram); | ||||
|  | ||||
|         this._buttonBox = new St.Widget({ layout_manager: new Clutter.BinLayout(), | ||||
|                                           x_expand: true, | ||||
|                                           x_align: Clutter.ActorAlign.CENTER, | ||||
|                                           y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this._editButton = new St.Button({ label: _('Edit...'), | ||||
|                                            style_class: 'button', | ||||
|                                            can_focus: true, | ||||
|                                            x_expand: true }); | ||||
|         this._editButton.connect('clicked', Lang.bind(this, function () { this.setEditionMode(true) })); | ||||
|         this._buttonBox.add_actor(this._editButton); | ||||
|         this.actor.add_actor(this._buttonBox); | ||||
|  | ||||
|         let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.VERTICAL }); | ||||
|         this._labelBox = new St.Widget({ layout_manager: boxLayout, | ||||
|                                          x_expand: true, | ||||
|                                          x_align: Clutter.ActorAlign.CENTER, | ||||
|                                          y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this._titleLabel = new St.Label(); | ||||
|         this._titleLabel.clutter_text.set_markup('<span size="larger"><b>' + padDevice.get_device_name() + '</b></span>'); | ||||
|         this._labelBox.add_actor(this._titleLabel); | ||||
|  | ||||
|         this._tipLabel = new St.Label(); | ||||
|         this._labelBox.add_actor(this._tipLabel); | ||||
|         this.actor.add_actor(this._labelBox); | ||||
|  | ||||
|         this._actionEditor = new ActionEditor(); | ||||
|         this._actionEditor.connect ('done', Lang.bind(this, this._endButtonActionEdition)); | ||||
|         this.actor.add_actor(this._actionEditor.actor); | ||||
|  | ||||
|         this._labels = []; | ||||
|         this._ringLabels = []; | ||||
|         this._stripLabels = []; | ||||
|  | ||||
|         // FIXME: Fix num buttons. | ||||
|         let i = 0; | ||||
|         for (i = 0; i < 50; i++) { | ||||
|             let [found, x, y, direction] = this._padDiagram.getButtonLabelCoords(i); | ||||
|             if (!found) | ||||
|                 break; | ||||
|             let label = this._createLabel(i, Meta.PadActionType.BUTTON); | ||||
|             this._labels.push(label); | ||||
|         } | ||||
|  | ||||
|         for (i = 0; i < padDevice.get_n_rings(); i++) { | ||||
|             let [found, x, y, direction] = this._padDiagram.getRingLabelCoords(i, CW); | ||||
|             let [found2, x2, y2, direction2] = this._padDiagram.getRingLabelCoords(i, CCW); | ||||
|             if (!found || !found2) | ||||
|                 break; | ||||
|  | ||||
|             let label1 = this._createLabel(i, Meta.PadActionType.RING); | ||||
|             let label2 = this._createLabel(i, Meta.PadActionType.RING); | ||||
|             this._ringLabels.push([label1, label2]); | ||||
|         } | ||||
|  | ||||
|         for (i = 0; i < padDevice.get_n_strips(); i++) { | ||||
|             let [found, x, y, direction] = this._padDiagram.getStripLabelCoords(i, UP); | ||||
|             let [found2, x2, y2, direction2] = this._padDiagram.getStripLabelCoords(i, DOWN); | ||||
|             if (!found || !found2) | ||||
|                 break; | ||||
|  | ||||
|             let label1 = this._createLabel(i, Meta.PadActionType.STRIP); | ||||
|             let label2 = this._createLabel(i, Meta.PadActionType.STRIP); | ||||
|             this._stripLabels.push([label1, label2]); | ||||
|         } | ||||
|  | ||||
|         this._syncEditionMode(); | ||||
|     }, | ||||
|  | ||||
|     _createLabel: function (number, type) { | ||||
|         let str = global.display.get_pad_action_label(this.padDevice, type, number); | ||||
|         let label = new St.Label({ text: str ? str : _('None') }); | ||||
|         this.actor.add_actor(label); | ||||
|  | ||||
|         return label; | ||||
|     }, | ||||
|  | ||||
|     _allocateChild: function (child, x, y, direction, box) { | ||||
|         let [prefHeight, natHeight] = child.get_preferred_height (-1); | ||||
|         let [prefWidth, natWidth] = child.get_preferred_width (natHeight); | ||||
|         let childBox = new Clutter.ActorBox(); | ||||
|  | ||||
|         natWidth = Math.min(natWidth, 250); | ||||
|  | ||||
|         if (direction == LTR) { | ||||
|             childBox.x1 = x + box.x1; | ||||
|             childBox.x2 = x + box.x1 + natWidth; | ||||
|         } else { | ||||
|             childBox.x1 = x + box.x1 - natWidth; | ||||
|             childBox.x2 = x + box.x1; | ||||
|         } | ||||
|  | ||||
|         childBox.y1 = y + box.y1 - natHeight / 2; | ||||
|         childBox.y2 = y + box.y1 + natHeight / 2; | ||||
|         child.allocate(childBox, 0); | ||||
|     }, | ||||
|  | ||||
|     _allocate: function (actor, box, flags) { | ||||
|         let [prefLabelHeight, natLabelHeight] = this._labelBox.get_preferred_height(box.x2 - box.x1); | ||||
|         let buttonY = Math.max((box.y2 - box.y1) * 3 / 4 + box.y1, (box.y2 - box.y1) - 100); | ||||
|         let childBox = new Clutter.ActorBox(); | ||||
|         let diagramBox = new Clutter.ActorBox(); | ||||
|  | ||||
|         diagramBox.x1 = box.x1; | ||||
|         diagramBox.x2 = box.x2; | ||||
|         diagramBox.y1 = prefLabelHeight; | ||||
|         diagramBox.y2 = buttonY; | ||||
|         this._padDiagram.allocate(diagramBox, flags); | ||||
|  | ||||
|         childBox.x1 = box.x1; | ||||
|         childBox.x2 = box.x2; | ||||
|         childBox.y1 = buttonY; | ||||
|         childBox.y2 = box.y2; | ||||
|         this._buttonBox.allocate(childBox, flags); | ||||
|  | ||||
|         childBox.y1 = 0; | ||||
|         childBox.y2 = prefLabelHeight; | ||||
|         this._labelBox.allocate(childBox, flags); | ||||
|  | ||||
|         for (let i = 0; i < this._labels.length; i++) { | ||||
|             let label = this._labels[i]; | ||||
|             let [found, x, y, direction] = this._padDiagram.getButtonLabelCoords(i); | ||||
|             this._allocateChild(label, x, y, direction, diagramBox); | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < this._ringLabels.length; i++) { | ||||
|             let [label1, label2] = this._ringLabels[i]; | ||||
|  | ||||
|             let [found, x, y, direction] = this._padDiagram.getRingLabelCoords(i, CW); | ||||
|             this._allocateChild(label1, x, y, direction, diagramBox); | ||||
|  | ||||
|             [found, x, y, direction] = this._padDiagram.getRingLabelCoords(i, CCW); | ||||
|             this._allocateChild(label2, x, y, direction, diagramBox); | ||||
|         } | ||||
|  | ||||
|         for (let i = 0; i < this._stripLabels.length; i++) { | ||||
|             let [label1, label2] = this._stripLabels[i]; | ||||
|  | ||||
|             let [found, x, y, direction] = this._padDiagram.getStripLabelCoords(i, UP); | ||||
|             this._allocateChild(label1, x, y, direction, diagramBox); | ||||
|  | ||||
|             [found, x, y, direction] = this._padDiagram.getStripLabelCoords(i, DOWN); | ||||
|             this._allocateChild(label2, x, y, direction, diagramBox); | ||||
|         } | ||||
|  | ||||
|         if (this._editingButtonAction != null) { | ||||
|             let [found, x, y, direction] = this._padDiagram.getButtonLabelCoords(this._editingButtonAction); | ||||
|             this._allocateChild(this._actionEditor.actor, x, y, direction, diagramBox); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onCapturedEvent : function (actor, event) { | ||||
|         if (event.type() == Clutter.EventType.PAD_BUTTON_PRESS && | ||||
|             event.get_source_device() == this.padDevice) { | ||||
|             this._padDiagram.activateButton(event.get_button()); | ||||
|  | ||||
|             if (this._editionMode) | ||||
|                 this._startButtonActionEdition(event.get_button()); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (event.type() == Clutter.EventType.PAD_BUTTON_RELEASE && | ||||
|                    event.get_source_device() == this.padDevice) { | ||||
|             this._padDiagram.deactivateButton(event.get_button()); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (event.type() == Clutter.EventType.KEY_PRESS && | ||||
|                    (!this._editionMode || event.get_key_symbol() == Clutter.Escape)) { | ||||
|             if (this._editingButtonAction != null) | ||||
|                 this._endButtonActionEdition(); | ||||
|             else | ||||
|                 this.destroy(); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _syncEditionMode: function () { | ||||
|         this._editButton.set_reactive(!this._editionMode); | ||||
|         this._editButton.save_easing_state(); | ||||
|         this._editButton.set_easing_duration(200); | ||||
|         this._editButton.set_opacity(this._editionMode ? 128 : 255); | ||||
|         this._editButton.restore_easing_state(); | ||||
|  | ||||
|         let title; | ||||
|  | ||||
|         if (this._editionMode) { | ||||
|             title = _('Press a button to configure'); | ||||
|             this._tipLabel.set_text (_("Press Esc to exit")); | ||||
|         } else { | ||||
|             title = this.padDevice.get_device_name(); | ||||
|             this._tipLabel.set_text (_("Press any key to exit")); | ||||
|         } | ||||
|  | ||||
|         this._titleLabel.clutter_text.set_markup('<span size="larger"><b>' + title + '</b></span>'); | ||||
|     }, | ||||
|  | ||||
|     _endButtonActionEdition: function () { | ||||
|         this._actionEditor.close(); | ||||
|  | ||||
|         if (this._editingButtonAction != null) { | ||||
|             // Update and show the label | ||||
|             let str = global.display.get_pad_action_label(this.padDevice, | ||||
|                                                           Meta.PadActionType.BUTTON, | ||||
|                                                           this._editingButtonAction); | ||||
|             this._labels[this._editingButtonAction].set_text(str ? str : _('None')); | ||||
|  | ||||
|             this._labels[this._editingButtonAction].show(); | ||||
|             this._editingButtonAction = null; | ||||
|         } | ||||
|  | ||||
|         this._editedButtonSettings = null; | ||||
|     }, | ||||
|  | ||||
|     _startButtonActionEdition: function (button) { | ||||
|         if (this._editingButtonAction == button) | ||||
|             return; | ||||
|  | ||||
|         this._endButtonActionEdition(); | ||||
|         this._editingButtonAction = button; | ||||
|  | ||||
|         this._labels[this._editingButtonAction].hide(); | ||||
|         this._actionEditor.actor.show(); | ||||
|         this.actor.queue_relayout(); | ||||
|  | ||||
|         let ch = String.fromCharCode('A'.charCodeAt() + button); | ||||
|         let settingsPath = this._settings.path + "button" + ch + '/'; | ||||
|         this._editedButtonSettings = Gio.Settings.new_with_path('org.gnome.desktop.peripherals.tablet.pad-button', | ||||
|                                                                 settingsPath); | ||||
|         this._actionEditor.setSettings (this._editedButtonSettings); | ||||
|     }, | ||||
|  | ||||
|     setEditionMode: function (editionMode) { | ||||
|         if (this._editionMode == editionMode) | ||||
|             return; | ||||
|  | ||||
|         this._editionMode = editionMode; | ||||
|         this._syncEditionMode(); | ||||
|     }, | ||||
|  | ||||
|     destroy: function () { | ||||
|         this._actionEditor.close(); | ||||
|  | ||||
|         if (this._capturedEventId != 0) { | ||||
|             global.stage.disconnect(this._capturedEventId); | ||||
|             this._capturedEventId = 0; | ||||
|         } | ||||
|  | ||||
|         if (this.actor) { | ||||
|             let actor = this.actor; | ||||
|             this.actor = null; | ||||
|             actor.destroy(); | ||||
|             this.emit('closed'); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(PadOsd.prototype); | ||||
|  | ||||
| const PadOsdIface = '<node> \ | ||||
| <interface name="org.gnome.Shell.Wacom.PadOsd"> \ | ||||
| <method name="Show"> \ | ||||
|     <arg name="device_node" direction="in" type="o"/> \ | ||||
|     <arg name="edition_mode" direction="in" type="b"/> \ | ||||
| </method> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const PadOsdService = new Lang.Class({ | ||||
|     Name: 'PadOsdService', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(PadOsdIface, this); | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Wacom'); | ||||
|         Gio.DBus.session.own_name('org.gnome.Shell.Wacom.PadOsd', Gio.BusNameOwnerFlags.REPLACE, null, null); | ||||
|     }, | ||||
|  | ||||
|     ShowAsync: function(params, invocation) { | ||||
|         let [deviceNode, editionMode] = params; | ||||
|         let deviceManager = Clutter.DeviceManager.get_default(); | ||||
|         let devices = deviceManager.list_devices(); | ||||
|         let padDevice = null; | ||||
|  | ||||
|         devices.forEach(Lang.bind(this, function(device) { | ||||
|             if (deviceNode == device.get_device_node()) | ||||
|                 padDevice = device; | ||||
|         })); | ||||
|  | ||||
|         if (padDevice == null || | ||||
|             padDevice.get_device_type() != Clutter.InputDeviceType.PAD_DEVICE) { | ||||
|             invocation.return_error_literal(Gio.IOErrorEnum, | ||||
|                                             Gio.IOErrorEnum.CANCELLED, | ||||
|                                             "Invalid params"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         global.display.request_pad_osd(padDevice, editionMode); | ||||
|         invocation.return_value(null); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(PadOsdService.prototype); | ||||
| @@ -17,6 +17,7 @@ const Main = imports.ui.main; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const WindowMenu = imports.ui.windowMenu; | ||||
| const PadOsd = imports.ui.padOsd; | ||||
|  | ||||
| const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings'; | ||||
| const MINIMIZE_WINDOW_ANIMATION_TIME = 0.2; | ||||
| @@ -915,6 +916,7 @@ const WindowManager = new Lang.Class({ | ||||
|                            Lang.bind(this, this._toggleCalendar)); | ||||
|  | ||||
|         global.display.connect('show-resize-popup', Lang.bind(this, this._showResizePopup)); | ||||
|         global.display.connect('show-pad-osd', Lang.bind(this, this._showPadOsd)); | ||||
|  | ||||
|         Main.overview.connect('showing', Lang.bind(this, function() { | ||||
|             for (let i = 0; i < this._dimmedWindows.length; i++) | ||||
| @@ -944,7 +946,19 @@ const WindowManager = new Lang.Class({ | ||||
|         gesture = new AppSwitchAction(); | ||||
|         gesture.connect('activated', Lang.bind(this, this._switchApp)); | ||||
|         global.stage.add_action(gesture); | ||||
|     }, | ||||
|  | ||||
|     _showPadOsd: function (display, device, settings, imagePath, editionMode, monitorIndex) { | ||||
|         if (this._currentPadOsd != null) { | ||||
|             if (this._currentPadOsd.padDevice == device) | ||||
|                 this._currentPadOsd.destroy(); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         this._currentPadOsd = new PadOsd.PadOsd(device, settings, imagePath, editionMode, monitorIndex); | ||||
|         this._currentPadOsd.connect('closed', Lang.bind(this, function() { this._currentPadOsd = null })); | ||||
|  | ||||
|         return this._currentPadOsd.actor; | ||||
|     }, | ||||
|  | ||||
|     _actionSwitchWorkspace: function(action, direction) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user