diff options
Diffstat (limited to 'js/ui/barLevel.js')
-rw-r--r-- | js/ui/barLevel.js | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/js/ui/barLevel.js b/js/ui/barLevel.js new file mode 100644 index 0000000..da5b34a --- /dev/null +++ b/js/ui/barLevel.js @@ -0,0 +1,262 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* exported BarLevel */ + +const { Atk, Clutter, GObject, St } = imports.gi; + +var BarLevel = GObject.registerClass({ + Properties: { + 'value': GObject.ParamSpec.double( + 'value', 'value', 'value', + GObject.ParamFlags.READWRITE, + 0, 2, 0), + 'maximum-value': GObject.ParamSpec.double( + 'maximum-value', 'maximum-value', 'maximum-value', + GObject.ParamFlags.READWRITE, + 1, 2, 1), + 'overdrive-start': GObject.ParamSpec.double( + 'overdrive-start', 'overdrive-start', 'overdrive-start', + GObject.ParamFlags.READWRITE, + 1, 2, 1), + }, +}, class BarLevel extends St.DrawingArea { + _init(params) { + this._maxValue = 1; + this._value = 0; + this._overdriveStart = 1; + this._barLevelWidth = 0; + + let defaultParams = { + style_class: 'barlevel', + accessible_role: Atk.Role.LEVEL_BAR, + }; + super._init(Object.assign(defaultParams, params)); + this.connect('notify::allocation', () => { + this._barLevelWidth = this.allocation.get_width(); + }); + + this._customAccessible = St.GenericAccessible.new_for_actor(this); + this.set_accessible(this._customAccessible); + + this._customAccessible.connect('get-current-value', this._getCurrentValue.bind(this)); + this._customAccessible.connect('get-minimum-value', this._getMinimumValue.bind(this)); + this._customAccessible.connect('get-maximum-value', this._getMaximumValue.bind(this)); + this._customAccessible.connect('set-current-value', this._setCurrentValue.bind(this)); + + this.connect('notify::value', this._valueChanged.bind(this)); + } + + get value() { + return this._value; + } + + set value(value) { + value = Math.max(Math.min(value, this._maxValue), 0); + + if (this._value == value) + return; + + this._value = value; + this.notify('value'); + this.queue_repaint(); + } + + get maximumValue() { + return this._maxValue; + } + + set maximumValue(value) { + value = Math.max(value, 1); + + if (this._maxValue == value) + return; + + this._maxValue = value; + this._overdriveStart = Math.min(this._overdriveStart, this._maxValue); + this.notify('maximum-value'); + this.queue_repaint(); + } + + get overdriveStart() { + return this._overdriveStart; + } + + set overdriveStart(value) { + if (this._overdriveStart == value) + return; + + if (value > this._maxValue) { + throw new Error(`Tried to set overdrive value to ${value}, ` + + `which is a number greater than the maximum allowed value ${this._maxValue}`); + } + + this._overdriveStart = value; + this.notify('overdrive-start'); + this.queue_repaint(); + } + + vfunc_repaint() { + let cr = this.get_context(); + let themeNode = this.get_theme_node(); + let [width, height] = this.get_surface_size(); + const rtl = this.get_text_direction() === Clutter.TextDirection.RTL; + + let barLevelHeight = themeNode.get_length('-barlevel-height'); + let barLevelBorderRadius = Math.min(width, barLevelHeight) / 2; + let fgColor = themeNode.get_foreground_color(); + + let barLevelColor = themeNode.get_color('-barlevel-background-color'); + let barLevelActiveColor = themeNode.get_color('-barlevel-active-background-color'); + let barLevelOverdriveColor = themeNode.get_color('-barlevel-overdrive-color'); + + let barLevelBorderWidth = Math.min(themeNode.get_length('-barlevel-border-width'), 1); + let [hasBorderColor, barLevelBorderColor] = + themeNode.lookup_color('-barlevel-border-color', false); + if (!hasBorderColor) + barLevelBorderColor = barLevelColor; + let [hasActiveBorderColor, barLevelActiveBorderColor] = + themeNode.lookup_color('-barlevel-active-border-color', false); + if (!hasActiveBorderColor) + barLevelActiveBorderColor = barLevelActiveColor; + let [hasOverdriveBorderColor, barLevelOverdriveBorderColor] = + themeNode.lookup_color('-barlevel-overdrive-border-color', false); + if (!hasOverdriveBorderColor) + barLevelOverdriveBorderColor = barLevelOverdriveColor; + + const TAU = Math.PI * 2; + + let endX = 0; + if (this._maxValue > 0) { + let progress = this._value / this._maxValue; + if (rtl) + progress = 1 - progress; + endX = barLevelBorderRadius + (width - 2 * barLevelBorderRadius) * progress; + } + + let overdriveRatio = this._overdriveStart / this._maxValue; + if (rtl) + overdriveRatio = 1 - overdriveRatio; + let overdriveSeparatorX = barLevelBorderRadius + (width - 2 * barLevelBorderRadius) * overdriveRatio; + + let overdriveActive = this._overdriveStart !== this._maxValue; + let overdriveSeparatorWidth = 0; + if (overdriveActive) + overdriveSeparatorWidth = themeNode.get_length('-barlevel-overdrive-separator-width'); + + let xcArcStart = barLevelBorderRadius + barLevelBorderWidth; + let xcArcEnd = width - xcArcStart; + if (rtl) + [xcArcStart, xcArcEnd] = [xcArcEnd, xcArcStart]; + + /* background bar */ + if (!rtl) + cr.arc(xcArcEnd, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4)); + else + cr.arcNegative(xcArcEnd, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4)); + cr.lineTo(endX, (height + barLevelHeight) / 2); + cr.lineTo(endX, (height - barLevelHeight) / 2); + cr.lineTo(xcArcEnd, (height - barLevelHeight) / 2); + Clutter.cairo_set_source_color(cr, barLevelColor); + cr.fillPreserve(); + Clutter.cairo_set_source_color(cr, barLevelBorderColor); + cr.setLineWidth(barLevelBorderWidth); + cr.stroke(); + + /* normal progress bar */ + let x = 0; + if (!rtl) { + x = Math.min(endX, overdriveSeparatorX - overdriveSeparatorWidth / 2); + cr.arc(xcArcStart, height / 2, barLevelBorderRadius, TAU * (1 / 4), TAU * (3 / 4)); + } else { + x = Math.max(endX, overdriveSeparatorX + overdriveSeparatorWidth / 2); + cr.arcNegative(xcArcStart, height / 2, barLevelBorderRadius, TAU * (1 / 4), TAU * (3 / 4)); + } + cr.lineTo(x, (height - barLevelHeight) / 2); + cr.lineTo(x, (height + barLevelHeight) / 2); + cr.lineTo(xcArcStart, (height + barLevelHeight) / 2); + if (this._value > 0) + Clutter.cairo_set_source_color(cr, barLevelActiveColor); + cr.fillPreserve(); + Clutter.cairo_set_source_color(cr, barLevelActiveBorderColor); + cr.setLineWidth(barLevelBorderWidth); + cr.stroke(); + + /* overdrive progress barLevel */ + if (!rtl) + x = Math.min(endX, overdriveSeparatorX) + overdriveSeparatorWidth / 2; + else + x = Math.max(endX, overdriveSeparatorX) - overdriveSeparatorWidth / 2; + if (this._value > this._overdriveStart) { + cr.moveTo(x, (height - barLevelHeight) / 2); + cr.lineTo(endX, (height - barLevelHeight) / 2); + cr.lineTo(endX, (height + barLevelHeight) / 2); + cr.lineTo(x, (height + barLevelHeight) / 2); + cr.lineTo(x, (height - barLevelHeight) / 2); + Clutter.cairo_set_source_color(cr, barLevelOverdriveColor); + cr.fillPreserve(); + Clutter.cairo_set_source_color(cr, barLevelOverdriveBorderColor); + cr.setLineWidth(barLevelBorderWidth); + cr.stroke(); + } + + /* end progress bar arc */ + if (this._value > 0) { + if (this._value <= this._overdriveStart) + Clutter.cairo_set_source_color(cr, barLevelActiveColor); + else + Clutter.cairo_set_source_color(cr, barLevelOverdriveColor); + if (!rtl) { + cr.arc(endX, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4)); + cr.lineTo(Math.floor(endX), (height + barLevelHeight) / 2); + cr.lineTo(Math.floor(endX), (height - barLevelHeight) / 2); + } else { + cr.arcNegative(endX, height / 2, barLevelBorderRadius, TAU * (3 / 4), TAU * (1 / 4)); + cr.lineTo(Math.ceil(endX), (height + barLevelHeight) / 2); + cr.lineTo(Math.ceil(endX), (height - barLevelHeight) / 2); + } + cr.lineTo(endX, (height - barLevelHeight) / 2); + cr.fillPreserve(); + cr.setLineWidth(barLevelBorderWidth); + cr.stroke(); + } + + /* draw overdrive separator */ + if (overdriveActive) { + cr.moveTo(overdriveSeparatorX - overdriveSeparatorWidth / 2, (height - barLevelHeight) / 2); + cr.lineTo(overdriveSeparatorX + overdriveSeparatorWidth / 2, (height - barLevelHeight) / 2); + cr.lineTo(overdriveSeparatorX + overdriveSeparatorWidth / 2, (height + barLevelHeight) / 2); + cr.lineTo(overdriveSeparatorX - overdriveSeparatorWidth / 2, (height + barLevelHeight) / 2); + cr.lineTo(overdriveSeparatorX - overdriveSeparatorWidth / 2, (height - barLevelHeight) / 2); + if (this._value <= this._overdriveStart) + Clutter.cairo_set_source_color(cr, fgColor); + else + Clutter.cairo_set_source_color(cr, barLevelColor); + cr.fill(); + } + + cr.$dispose(); + } + + _getCurrentValue() { + return this._value; + } + + _getOverdriveStart() { + return this._overdriveStart; + } + + _getMinimumValue() { + return 0; + } + + _getMaximumValue() { + return this._maxValue; + } + + _setCurrentValue(_actor, value) { + this._value = value; + } + + _valueChanged() { + this._customAccessible.notify("accessible-value"); + } +}); |