From 028a2d25f7a84645b583bef597b87d71964d564e 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: toggleSwitch: Make handle draggable --- js/ui/popupMenu.js | 112 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 103 insertions(+), 9 deletions(-) diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index 486f63fcb..be9715d89 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -328,12 +328,14 @@ var Switch = GObject.registerClass({ false), }, }, class Switch extends St.Widget { - _init(state) { + _init(state, reactive = true) { this._state = false; + this._dragging = false; super._init({ style_class: 'toggle-switch', accessible_role: Atk.Role.CHECK_BOX, + reactive, }); this._iconOn = new St.Icon({ @@ -365,12 +367,15 @@ var Switch = GObject.registerClass({ source: this, coordinate: Clutter.BindCoordinate.HEIGHT, }), + 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; @@ -381,9 +386,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() @@ -401,13 +403,99 @@ 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(); + + this._grab = global.stage.grab(this); + + this._grabbedDevice = device; + this._grabbedSequence = sequence; + + return Clutter.EVENT_STOP; + } + + vfunc_motion_event() { + if (this._dragging && !this._grabbedSequence) + return this._motionEvent(this, Clutter.get_current_event()); + + return Clutter.EVENT_PROPAGATE; + } + + vfunc_button_release_event() { + if (this._dragging && !this._grabbedSequence) + return this._endDragging(); + + return Clutter.EVENT_PROPAGATE; + } + + _touchDragging(actor, event) { + let sequence = event.get_event_sequence(); + + if (!this._dragging && + event.type() === Clutter.EventType.TOUCH_BEGIN) { + this.startDragging(event); + return Clutter.EVENT_STOP; + } else if (this._grabbedSequence && + sequence.get_slot() === this._grabbedSequence.get_slot()) { + if (event.type() === Clutter.EventType.TOUCH_UPDATE) + return this._motionEvent(this, event); + else if (event.type() === Clutter.EventType.TOUCH_END) + return this._endDragging(); + } + + return Clutter.EVENT_PROPAGATE; + } + + _endDragging() { + if (this._dragging) { + if (this._grab) { + this._grab.dismiss(); + this._grab = null; + } + + 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({ @@ -421,7 +509,10 @@ var PopupSwitchMenuItem = GObject.registerClass({ y_expand: true, y_align: Clutter.ActorAlign.CENTER, }); - 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(); @@ -473,8 +564,6 @@ var PopupSwitchMenuItem = GObject.registerClass({ toggle() { this._switch.toggle(); - this.emit('toggled', this._switch.state); - this.checkAccessibleState(); } get state() { @@ -486,6 +575,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: -- cgit v1.2.1