diff --git a/data/com.vixalien.decibels.data.gresource.xml b/data/com.vixalien.decibels.data.gresource.xml index 60c3afb..2c5b585 100644 --- a/data/com.vixalien.decibels.data.gresource.xml +++ b/data/com.vixalien.decibels.data.gresource.xml @@ -11,6 +11,7 @@ header.ui player.ui playback-rate-button.ui + volume-button.ui window.ui gtk/help-overlay.ui diff --git a/data/meson.build b/data/meson.build index 13b72d8..dfa5c93 100644 --- a/data/meson.build +++ b/data/meson.build @@ -5,8 +5,9 @@ blueprints = custom_target('blueprints', 'empty.blp', 'error.blp', 'header.blp', - 'window.blp', 'playback-rate-button.blp', + 'volume-button.blp', + 'window.blp', ), output: '.', command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'], diff --git a/data/player.ui b/data/player.ui index 40c653d..efcc5ab 100644 --- a/data/player.ui +++ b/data/player.ui @@ -139,7 +139,7 @@ - + 3 diff --git a/data/volume-button.blp b/data/volume-button.blp new file mode 100644 index 0000000..b5b68a3 --- /dev/null +++ b/data/volume-button.blp @@ -0,0 +1,46 @@ +using Gtk 4.0; +using Adw 1; + +template $APVolumeButton : Adw.Bin { + MenuButton menu_button { + popover: + Popover { + Box { + margin-start: 4; + margin-end: 4; + margin-top: 4; + margin-bottom: 4; + spacing: 4; + orientation: vertical; + + Scale { + height-request: 200; + vexpand: true; + orientation: vertical; + inverted: true; + adjustment: + Adjustment adjustment { + step-increment: 0.1; + lower: 0; + upper: 1; + value: 0.5; + } + + ; + change-value => $scale_change_value_cb(); + + marks [ + mark (0.5, bottom), + ] + } + } + } + + ; + + styles [ + "flat", + "numeric", + ] + } +} diff --git a/po/POTFILES b/po/POTFILES index 8e426e3..c0bd54d 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -12,5 +12,6 @@ src/application.ts src/drag-overlay.ts src/error.ts src/stream.ts +src/volume-button.ts src/player.ts src/window.ts diff --git a/src/com.vixalien.decibels.src.gresource.xml.in b/src/com.vixalien.decibels.src.gresource.xml.in index c24e2e2..cd64790 100644 --- a/src/com.vixalien.decibels.src.gresource.xml.in +++ b/src/com.vixalien.decibels.src.gresource.xml.in @@ -11,6 +11,7 @@ playback-rate-button.js player.js stream.js + volume-button.js waveform.js window.js diff --git a/src/meson.build b/src/meson.build index 2dc1e65..7a47e9b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,6 +11,7 @@ sources = [ 'playback-rate-button.ts', 'player.ts', 'stream.ts', + 'volume-button.ts', 'waveform.ts', 'window.ts', ] diff --git a/src/player.ts b/src/player.ts index 7b6a801..2f7ee18 100644 --- a/src/player.ts +++ b/src/player.ts @@ -7,8 +7,10 @@ import { Window } from "./window.js"; import { APHeaderBar } from "./header.js"; import { APWaveForm } from "./waveform.js"; import { APPlaybackRateButton } from "./playback-rate-button.js"; +import { APVolumeButton } from "./volume-button.js"; GObject.type_ensure(APPlaybackRateButton.$gtype); +GObject.type_ensure(APVolumeButton.$gtype); export class APPlayerState extends Adw.Bin { private _scale_adjustment!: Gtk.Adjustment; diff --git a/src/volume-button.ts b/src/volume-button.ts new file mode 100644 index 0000000..cd1b727 --- /dev/null +++ b/src/volume-button.ts @@ -0,0 +1,93 @@ +import Adw from "gi://Adw"; +import Gtk from "gi://Gtk?version=4.0"; +import GObject from "gi://GObject"; + +export class APVolumeButton extends Adw.Bin { + private _adjustment!: Gtk.Adjustment; + private _menu_button!: Gtk.MenuButton; + + static { + GObject.registerClass( + { + GTypeName: "APVolumeButton", + Template: "resource:///com/vixalien/decibels/volume-button.ui", + InternalChildren: [ + "adjustment", + "menu_button", + ], + Properties: { + value: GObject.param_spec_double( + "value", + "value", + "The current value of the VolumeButton", + 0, + 1.0, + 0.5, + GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE | + GObject.ParamFlags.EXPLICIT_NOTIFY, + ), + }, + }, + this, + ); + } + + constructor(params?: Partial) { + super(params); + } + + get value() { + return this._adjustment.value; + } + + set value(val: number) { + if (val === this.value) return; + + this._adjustment.value = val; + this.set_tooltip(val); + this.set_icon(val); + + this.notify("value"); + } + + private set_tooltip(value: number) { + let tooltip; + + if (value === 1) { + tooltip = _("Full Volume"); + } else if (value === 0) { + tooltip = _("Muted"); + } else { + tooltip = imports.format.vprintf( + /* Translators: this is the percentage of the current volume, + * as used in the tooltip, eg. "49 %". + * Translate the "%d" to "%Id" if you want to use localised digits, + * or otherwise translate the "%d" to "%d". + */ + C_("volume percentage", "%d %%"), + [Math.round(100 * value).toString()], + ); + } + + this.set_tooltip_text(tooltip); + } + + private set_icon(value: number) { + let icon_name: string; + + if (value === 0) icon_name = "audio-volume-muted"; + else if (value === 1) icon_name = "audio-volume-high"; + else if (value <= 0.5) icon_name = "audio-volume-low"; + else icon_name = "audio-volume-medium"; + + this._menu_button.icon_name = `${icon_name}-symbolic`; + } + + private scale_change_value_cb( + _scale: Gtk.Scale, + _scroll: Gtk.ScrollType, + value: number, + ) { + this.value = value; + } +} diff --git a/types/ambient.d.ts b/types/ambient.d.ts index 2145882..b9bbc27 100644 --- a/types/ambient.d.ts +++ b/types/ambient.d.ts @@ -1,4 +1,5 @@ declare function _(id: string): string; +declare function C_(ctx: string, id: string): string; declare function print(args: string): void; declare function log(obj: object, others?: object[]): void; declare function log(msg: string, substitutions?: any[]): void; @@ -16,10 +17,10 @@ declare module console { export function debug(...args: any[]): void; } -declare interface String { - format(...replacements: string[]): string; - format(...replacements: number[]): string; +declare module imports { + const format: { + format(this: String, ...args: any[]): string; + printf(fmt: string, ...args: any[]): string; + vprintf(fmt: string, args: any[]): string; + }; } -declare interface Number { - toFixed(digits: number): number; -} \ No newline at end of file