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 @@
-
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