From d797afbc2e376c0745c3bd22cc7dfe17e81cd0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ruiz=20de=20Alegr=C3=ADa?= Date: Tue, 7 May 2019 15:20:59 +0200 Subject: [PATCH 1/3] theme: Style switches with pure CSS --- data/gnome-shell-theme.gresource.xml | 6 - data/theme/gnome-shell-high-contrast.scss | 7 +- .../gnome-shell-sass/widgets/_switches.scss | 33 +++-- data/theme/toggle-off-dark.svg | 1 - data/theme/toggle-off-hc.svg | 133 ------------------ data/theme/toggle-off.svg | 1 - data/theme/toggle-on-dark.svg | 1 - data/theme/toggle-on-hc.svg | 113 --------------- data/theme/toggle-on.svg | 1 - js/ui/popupMenu.js | 19 ++- 10 files changed, 42 insertions(+), 273 deletions(-) delete mode 100644 data/theme/toggle-off-dark.svg delete mode 100644 data/theme/toggle-off-hc.svg delete mode 100644 data/theme/toggle-off.svg delete mode 100644 data/theme/toggle-on-dark.svg delete mode 100644 data/theme/toggle-on-hc.svg delete mode 100644 data/theme/toggle-on.svg diff --git a/data/gnome-shell-theme.gresource.xml b/data/gnome-shell-theme.gresource.xml index 038c87cf07..a297df19d9 100644 --- a/data/gnome-shell-theme.gresource.xml +++ b/data/gnome-shell-theme.gresource.xml @@ -28,11 +28,5 @@ keyboard-layout-filled-symbolic.svg keyboard-shift-filled-symbolic.svg process-working.svg - toggle-off.svg - toggle-off-dark.svg - toggle-off-hc.svg - toggle-on.svg - toggle-on-dark.svg - toggle-on-hc.svg diff --git a/data/theme/gnome-shell-high-contrast.scss b/data/theme/gnome-shell-high-contrast.scss index a6a5314cf2..01f895b6e2 100644 --- a/data/theme/gnome-shell-high-contrast.scss +++ b/data/theme/gnome-shell-high-contrast.scss @@ -10,10 +10,9 @@ stage { -st-icon-style: symbolic; } -.toggle-switch { width: 48px; } -.toggle-switch-us, .toggle-switch-intl { - background-image: url("resource:///org/gnome/shell/theme/toggle-off-hc.svg"); - &:checked { background-image: url("resource:///org/gnome/shell/theme/toggle-on-hc.svg"); } +.toggle-switch { + background: lighten($bg_color, 30%); + .handle { background-color: #FFF; } } //force opaque panel diff --git a/data/theme/gnome-shell-sass/widgets/_switches.scss b/data/theme/gnome-shell-sass/widgets/_switches.scss index fd7472ed3c..ca7d50bbfd 100644 --- a/data/theme/gnome-shell-sass/widgets/_switches.scss +++ b/data/theme/gnome-shell-sass/widgets/_switches.scss @@ -1,16 +1,31 @@ /* Switches */ -// these are equal to the size of the SVG assets -$switch_height: 22px; -$switch_width: 46px; +$switch_height: 1.5em; +$switch_width: 2.86em; +$switch_handle_border: 0.07em; .toggle-switch { - color: $fg_color; + $_handle_size: $switch_height - 2 * $switch_handle_border; height: $switch_height; width: $switch_width; - background-size: contain; - background-image: if($variant == 'light', url("resource:///org/gnome/shell/theme/toggle-off.svg"),url("resource:///org/gnome/shell/theme/toggle-off-dark.svg")); - &:checked { - background-image: if($variant == 'light', url("resource:///org/gnome/shell/theme/toggle-on.svg"),url("resource:///org/gnome/shell/theme/toggle-on-dark.svg")); + border-radius: $switch_height; + background: mix($borders_color, $bg_color, 50%); + box-shadow: inset 0 0 0 1px $borders_color; + transition-duration: 250ms; + + .handle { + width: $_handle_size; + height: $_handle_size; + border-radius: $_handle_size; + background: lighten($bg_color, 3%); + border: $switch_handle_border solid darken($bg_color, 14%); + } + + &:checked { + background: $selected_bg_color; + box-shadow: inset 0 0 0 1px darken($selected_bg_color, 25%); + .handle { + @if $variant == 'light' { border-color: darken($selected_bg_color, 15%); } + } } -} \ No newline at end of file +} diff --git a/data/theme/toggle-off-dark.svg b/data/theme/toggle-off-dark.svg deleted file mode 100644 index 163a135dca..0000000000 --- a/data/theme/toggle-off-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/data/theme/toggle-off-hc.svg b/data/theme/toggle-off-hc.svg deleted file mode 100644 index 809cd3f7b7..0000000000 --- a/data/theme/toggle-off-hc.svg +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - diff --git a/data/theme/toggle-off.svg b/data/theme/toggle-off.svg deleted file mode 100644 index ee51782943..0000000000 --- a/data/theme/toggle-off.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/data/theme/toggle-on-dark.svg b/data/theme/toggle-on-dark.svg deleted file mode 100644 index b71931ac84..0000000000 --- a/data/theme/toggle-on-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/data/theme/toggle-on-hc.svg b/data/theme/toggle-on-hc.svg deleted file mode 100644 index 9952f95407..0000000000 --- a/data/theme/toggle-on-hc.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - diff --git a/data/theme/toggle-on.svg b/data/theme/toggle-on.svg deleted file mode 100644 index 28be67e289..0000000000 --- a/data/theme/toggle-on.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index 11528560d4..57a6ae1cc1 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -317,7 +317,7 @@ var Switch = GObject.registerClass({ GObject.ParamFlags.READWRITE, false), }, -}, class Switch extends St.Bin { +}, class Switch extends St.BoxLayout { _init(state) { this._state = false; @@ -325,8 +325,16 @@ var Switch = GObject.registerClass({ style_class: 'toggle-switch', accessible_role: Atk.Role.CHECK_BOX, can_focus: true, - state, }); + + this._handle = new St.Widget({ + style_class: 'handle', + x_expand: true, + x_align: Clutter.ActorAlign.START, + }); + this.add_child(this._handle); + + this.state = state; } get state() { @@ -337,10 +345,13 @@ var Switch = GObject.registerClass({ if (this._state === state) return; - if (state) + if (state) { this.add_style_pseudo_class('checked'); - else + this._handle.x_align = Clutter.ActorAlign.END; + } else { this.remove_style_pseudo_class('checked'); + this._handle.x_align = Clutter.ActorAlign.START; + } this._state = state; this.notify('state'); -- GitLab From 4f71849158ab7f31fa3ed2a4954082e8c1fe1d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ruiz=20de=20Alegr=C3=ADa?= Date: Tue, 7 May 2019 16:23:11 +0200 Subject: [PATCH 2/3] toggleSwitch: Animate transitions --- .../gnome-shell-sass/widgets/_switches.scss | 4 +++- js/ui/popupMenu.js | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/data/theme/gnome-shell-sass/widgets/_switches.scss b/data/theme/gnome-shell-sass/widgets/_switches.scss index ca7d50bbfd..b8ca5b05dd 100644 --- a/data/theme/gnome-shell-sass/widgets/_switches.scss +++ b/data/theme/gnome-shell-sass/widgets/_switches.scss @@ -6,12 +6,13 @@ $switch_handle_border: 0.07em; .toggle-switch { $_handle_size: $switch_height - 2 * $switch_handle_border; + $_transition-duration: 250ms; height: $switch_height; width: $switch_width; border-radius: $switch_height; background: mix($borders_color, $bg_color, 50%); box-shadow: inset 0 0 0 1px $borders_color; - transition-duration: 250ms; + transition-duration: $_transition-duration; .handle { width: $_handle_size; @@ -19,6 +20,7 @@ $switch_handle_border: 0.07em; border-radius: $_handle_size; background: lighten($bg_color, 3%); border: $switch_handle_border solid darken($bg_color, 14%); + transition-duration: $_transition-duration; } &:checked { diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index 57a6ae1cc1..df2520fef8 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -327,11 +327,12 @@ var Switch = GObject.registerClass({ can_focus: true, }); - this._handle = new St.Widget({ - style_class: 'handle', - x_expand: true, - x_align: Clutter.ActorAlign.START, + this._handle = new St.Widget({ style_class: 'handle' }); + this._handleAlignConstraint = new Clutter.AlignConstraint({ + align_axis: Clutter.AlignAxis.X_AXIS, + source: this, }); + this._handle.add_constraint_with_name('align', this._handleAlignConstraint); this.add_child(this._handle); this.state = state; @@ -345,14 +346,23 @@ var Switch = GObject.registerClass({ if (this._state === state) return; + let handleAlignFactor; + const duration = this._handle.mapped + ? this._handle.get_theme_node().get_transition_duration() + : 0; + if (state) { this.add_style_pseudo_class('checked'); - this._handle.x_align = Clutter.ActorAlign.END; + handleAlignFactor = 1.0; } else { this.remove_style_pseudo_class('checked'); - this._handle.x_align = Clutter.ActorAlign.START; + handleAlignFactor = 0.0; } + this._handle.ease_property('@constraints.align.factor', handleAlignFactor, { + duration, + }); + this._state = state; this.notify('state'); } -- GitLab From 76d3a48c7351acef887c22d35c6c4ab079a97d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ruiz=20de=20Alegr=C3=ADa?= Date: Tue, 21 Jan 2020 11:30:42 +0100 Subject: [PATCH 3/3] toggleSwitch: Make handle draggable --- js/ui/popupMenu.js | 117 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 10 deletions(-) diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index df2520fef8..3a1299ed89 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -318,8 +318,9 @@ var Switch = GObject.registerClass({ false), }, }, class Switch extends St.BoxLayout { - _init(state) { + _init(state, reactive = true) { this._state = false; + this._dragging = false; super._init({ style_class: 'toggle-switch', @@ -327,12 +328,17 @@ var Switch = GObject.registerClass({ can_focus: true, }); - this._handle = new St.Widget({ style_class: 'handle' }); + this._handle = new St.Widget({ + style_class: 'handle', + reactive, + }); this._handleAlignConstraint = new Clutter.AlignConstraint({ align_axis: Clutter.AlignAxis.X_AXIS, source: this, }); this._handle.add_constraint_with_name('align', this._handleAlignConstraint); + this._handle.connect('button-press-event', (actor, event) => this._startDragging(event)); + this._handle.connect('touch-event', this._touchDragging.bind(this)); this.add_child(this._handle); this.state = state; @@ -343,9 +349,6 @@ var Switch = GObject.registerClass({ } set state(state) { - if (this._state === state) - return; - let handleAlignFactor; const duration = this._handle.mapped ? this._handle.get_theme_node().get_transition_duration() @@ -363,13 +366,101 @@ var Switch = GObject.registerClass({ duration, }); - this._state = state; - this.notify('state'); + if (this._state !== state) { + this._state = state; + this.notify('state'); + } } toggle() { this.state = !this.state; } + + _startDragging(event) { + if (this._dragging) + return Clutter.EVENT_PROPAGATE; + + this._dragging = true; + [this._initialGrabX] = event.get_coords(); + let device = event.get_device(); + let sequence = event.get_event_sequence(); + + if (sequence != null) + device.sequence_grab(sequence, this); + else + device.grab(this); + + this._grabbedDevice = device; + this._grabbedSequence = sequence; + + if (sequence == null) { + this._releaseId = this.connect('button-release-event', this._endDragging.bind(this)); + this._motionId = this.connect('motion-event', this._motionEvent.bind(this)); + } + + return Clutter.EVENT_STOP; + } + + _touchDragging(actor, event) { + let device = event.get_device(); + let sequence = event.get_event_sequence(); + + if (!this._dragging && event.type() == Clutter.EventType.TOUCH_BEGIN) { + return this._startDragging(event); + } else if (device.sequence_get_grabbed_actor(sequence) == actor) { + if (event.type() == Clutter.EventType.TOUCH_UPDATE) + return this._motionEvent(actor, event); + else if (event.type() == Clutter.EventType.TOUCH_END || + event.type() == Clutter.EventType.TOUCH_CANCEL) + return this._endDragging(); + } + + return Clutter.EVENT_PROPAGATE; + } + + _endDragging() { + if (this._dragging) { + if (this._releaseId) { + this.disconnect(this._releaseId); + this._releaseId = null; + } + if (this._motionId) { + this.disconnect(this._motionId); + this._motionId = null; + } + + if (this._grabbedSequence != null) + this._grabbedDevice.sequence_ungrab(this._grabbedSequence); + else + this._grabbedDevice.ungrab(); + + if (this._dragged == null) + this.toggle(); + else + this.state = this._handleAlignConstraint.get_factor() > 0.5; + + this._dragged = null; + this._grabbedSequence = null; + this._grabbedDevice = null; + this._dragging = false; + } + return Clutter.EVENT_STOP; + } + + _motionEvent(actor, event) { + this._dragged = true; + + let [absX] = event.get_coords(); + let factorDiff = (absX - this._initialGrabX) / (this.get_width() - this._handle.get_width()); + let factor = factorDiff + (this.state ? 1.0 : 0.0); + + factor = Math.max(factor, 0.0); + factor = Math.min(factor, 1.0); + + this._handleAlignConstraint.set_factor(factor); + + return Clutter.EVENT_STOP; + } }); var PopupSwitchMenuItem = GObject.registerClass({ @@ -379,7 +470,10 @@ var PopupSwitchMenuItem = GObject.registerClass({ super._init(params); this.label = new St.Label({ text }); - this._switch = new Switch(active); + + let switchReactive = params && params.reactive; + this._switch = new Switch(active, switchReactive); + this._switch.connect('notify::state', this._onToggled.bind(this)); this.accessible_role = Atk.Role.CHECK_MENU_ITEM; this.checkAccessibleState(); @@ -429,8 +523,6 @@ var PopupSwitchMenuItem = GObject.registerClass({ toggle() { this._switch.toggle(); - this.emit('toggled', this._switch.state); - this.checkAccessibleState(); } get state() { @@ -442,6 +534,11 @@ var PopupSwitchMenuItem = GObject.registerClass({ this.checkAccessibleState(); } + _onToggled(sw, state) { + this.emit('toggled', state); + this.checkAccessibleState(); + } + checkAccessibleState() { switch (this.accessible_role) { case Atk.Role.CHECK_MENU_ITEM: -- GitLab