Files
.settings
browser-plugin
data
docs
js
extensionPrefs
gdm
misc
perf
portalHelper
ui
components
status
accessDialog.js
altTab.js
animation.js
appDisplay.js
appFavorites.js
audioDeviceSelection.js
background.js
backgroundMenu.js
barLevel.js
boxpointer.js
calendar.js
checkBox.js
closeDialog.js
ctrlAltTab.js
dash.js
dateMenu.js
dialog.js
dnd.js
edgeDragAction.js
endSessionDialog.js
environment.js
extensionDownloader.js
extensionSystem.js
focusCaretTracker.js
grabHelper.js
ibusCandidatePopup.js
iconGrid.js
inhibitShortcutsDialog.js
kbdA11yDialog.js
keyboard.js
layout.js
lightbox.js
lookingGlass.js
magnifier.js
magnifierDBus.js
main.js
messageList.js
messageTray.js
modalDialog.js
mpris.js
notificationDaemon.js
osdMonitorLabeler.js
osdWindow.js
overview.js
overviewControls.js
padOsd.js
panel.js
panelMenu.js
pointerWatcher.js
popupMenu.js
remoteMenu.js
remoteSearch.js
runDialog.js
screenShield.js
screencast.js
screenshot.js
scripting.js
search.js
sessionMode.js
shellDBus.js
shellEntry.js
shellMountOperation.js
slider.js
switchMonitor.js
switcherPopup.js
tweener.js
unlockDialog.js
userWidget.js
viewSelector.js
windowAttentionHandler.js
windowManager.js
windowMenu.js
workspace.js
workspaceSwitcherPopup.js
workspaceThumbnail.js
workspacesView.js
xdndHandler.js
js-resources.gresource.xml
meson.build
portal-resources.gresource.xml
prefs-resources.gresource.xml
man
meson
po
src
subprojects
tests
tools
.gitignore
.gitmodules
.project
COPYING
HACKING.md
NEWS
README.md
config.h.meson
gnome-shell.doap
meson.build
meson_options.txt
gnome-shell/js/ui/slider.js
Didier Roche 3f756dc608 barLevel: Support maxValue higher than 1
Ensure that both barLevel and slider can support a higher maxValue than 1
and computes various positions based on it.
It defaults to 1 if not set.

https://bugzilla.gnome.org/show_bug.cgi?id=790280.
2018-07-31 18:14:10 +00:00

221 lines
7.0 KiB
JavaScript

/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Atk = imports.gi.Atk;
const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const St = imports.gi.St;
const Signals = imports.signals;
const BarLevel = imports.ui.barLevel;
var SLIDER_SCROLL_STEP = 0.02; /* Slider scrolling step in % */
var Slider = new Lang.Class({
Name: "Slider",
Extends: BarLevel.BarLevel,
_init(value) {
let params = {
styleClass: 'slider',
canFocus: true,
reactive: true,
accessibleRole: Atk.Role.SLIDER,
}
this.parent(value, params)
this.actor.connect('button-press-event', this._startDragging.bind(this));
this.actor.connect('touch-event', this._touchDragging.bind(this));
this.actor.connect('scroll-event', this._onScrollEvent.bind(this));
this.actor.connect('key-press-event', this.onKeyPressEvent.bind(this));
this._releaseId = this._motionId = 0;
this._dragging = false;
this._customAccessible.connect('get-minimum-increment', this._getMinimumIncrement.bind(this));
},
_barLevelRepaint(area) {
this.parent(area);
// Add handle
let cr = area.get_context();
let themeNode = area.get_theme_node();
let [width, height] = area.get_surface_size();
let handleRadius = themeNode.get_length('-slider-handle-radius');
let handleBorderWidth = themeNode.get_length('-slider-handle-border-width');
let [hasHandleColor, handleBorderColor] =
themeNode.lookup_color('-slider-handle-border-color', false);
const TAU = Math.PI * 2;
let handleX = handleRadius + (width - 2 * handleRadius) * this._value / this._maxValue;
let handleY = height / 2;
let color = themeNode.get_foreground_color();
Clutter.cairo_set_source_color(cr, color);
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
cr.fillPreserve();
if (hasHandleColor && handleBorderWidth) {
Clutter.cairo_set_source_color(cr, handleBorderColor);
cr.setLineWidth(handleBorderWidth);
cr.stroke();
}
cr.$dispose();
},
_startDragging(actor, event) {
return this.startDragging(event);
},
startDragging(event) {
if (this._dragging)
return Clutter.EVENT_PROPAGATE;
this._dragging = true;
let device = event.get_device();
let sequence = event.get_event_sequence();
if (sequence != null)
device.sequence_grab(sequence, this.actor);
else
device.grab(this.actor);
this._grabbedDevice = device;
this._grabbedSequence = sequence;
if (sequence == null) {
this._releaseId = this.actor.connect('button-release-event', this._endDragging.bind(this));
this._motionId = this.actor.connect('motion-event', this._motionEvent.bind(this));
}
// We need to emit 'drag-begin' before moving the handle to make
// sure that no 'value-changed' signal is emitted before this one.
this.emit('drag-begin');
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return Clutter.EVENT_STOP;
},
_endDragging() {
if (this._dragging) {
if (this._releaseId)
this.actor.disconnect(this._releaseId);
if (this._motionId)
this.actor.disconnect(this._motionId);
if (this._grabbedSequence != null)
this._grabbedDevice.sequence_ungrab(this._grabbedSequence);
else
this._grabbedDevice.ungrab();
this._grabbedSequence = null;
this._grabbedDevice = null;
this._dragging = false;
this.emit('drag-end');
}
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) {
this.startDragging(event);
return Clutter.EVENT_STOP;
} 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)
return this._endDragging();
}
return Clutter.EVENT_PROPAGATE;
},
scroll(event) {
let direction = event.get_scroll_direction();
let delta;
if (event.is_pointer_emulated())
return Clutter.EVENT_PROPAGATE;
if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SLIDER_SCROLL_STEP;
} else if (direction == Clutter.ScrollDirection.UP) {
delta = +SLIDER_SCROLL_STEP;
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
let [dx, dy] = event.get_scroll_delta();
// Even though the slider is horizontal, use dy to match
// the UP/DOWN above.
delta = -dy * SLIDER_SCROLL_STEP;
}
this._value = Math.min(Math.max(0, this._value + delta), this._maxValue);
this.actor.queue_repaint();
this.emit('value-changed', this._value);
return Clutter.EVENT_STOP;
},
_onScrollEvent(actor, event) {
return this.scroll(event);
},
_motionEvent(actor, event) {
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return Clutter.EVENT_STOP;
},
onKeyPressEvent(actor, event) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) {
let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
this._value = Math.max(0, Math.min(this._value + delta, this._maxValue));
this.actor.queue_repaint();
this.emit('drag-begin');
this.emit('value-changed', this._value);
this.emit('drag-end');
return Clutter.EVENT_STOP;
}
return Clutter.EVENT_PROPAGATE;
},
_moveHandle(absX, absY) {
let relX, relY, sliderX, sliderY;
[sliderX, sliderY] = this.actor.get_transformed_position();
relX = absX - sliderX;
relY = absY - sliderY;
let width = this._barLevelWidth;
let handleRadius = this.actor.get_theme_node().get_length('-slider-handle-radius');
let newvalue;
if (relX < handleRadius)
newvalue = 0;
else if (relX > width - handleRadius)
newvalue = 1;
else
newvalue = (relX - handleRadius) / (width - 2 * handleRadius);
this._value = newvalue * this._maxValue;
this.actor.queue_repaint();
this.emit('value-changed', this._value);
},
_getMinimumIncrement(actor) {
return 0.1;
},
});
Signals.addSignalMethods(Slider.prototype);