azalea_shell/window/taskbar/widget/
audio.rs1use azalea_service::{LocalListenerHandle, StaticServiceManager};
2use gtk::prelude::*;
3use relm4::RelmWidgetExt;
4use relm4::{ComponentParts, ComponentSender, SimpleComponent, component};
5
6use crate::icon;
7
8use crate::service;
9
10crate::init! {
11 Model {
12 system_volume: f64,
13 _service_handle: LocalListenerHandle,
14 }
15
16 Config {
17 }
18}
19
20#[derive(Debug)]
21pub enum Input {
22 Scroll(f64),
23 Audio(service::audio::Output),
24}
25
26#[component(pub)]
27impl SimpleComponent for Model {
28 type Init = Init;
29 type Input = Input;
30 type Output = ();
31
32 view! {
33 gtk::Box {
34 inline_css: "padding: 2px 8px 2px 4px;",
35 set_valign: gtk::Align::Center,
36 set_css_classes: &[
37 "azalea-secondary-border",
38 "azalea-circle-bubble",
39 "azalea-secondary-container",
40 "azalea-secondary-container-hover",
41 ],
42 set_spacing: 8,
43
44 gtk::Image {
45 set_icon_name: Some(icon::AUDIO),
46 },
47
48 gtk::Label {
49 #[watch]
50 set_label: &format!("{}%", model.system_volume_percent()),
51 },
52
53 gtk::Frame {
54 set_width_request: 100,
55 set_height_request: 5,
56 set_vexpand: false,
57 set_valign: gtk::Align::Center,
58
59 #[watch]
60 inline_css: &format!(
61 "background-image: linear-gradient(to right, var(--secondary) {}%, var(--on-secondary) 0);",
62 model.system_volume_percent()
63 ),
64 },
65
66 add_controller = gtk::EventControllerScroll {
67 set_flags: gtk::EventControllerScrollFlags::BOTH_AXES,
68 connect_scroll[sender] => move |_this, _dx, dy| {
69 sender.input(Input::Scroll(dy));
70 false.into()
71 },
72 },
73 },
74 }
75
76 fn init(
77 _init: Self::Init,
78 _root: Self::Root,
79 sender: ComponentSender<Self>,
80 ) -> ComponentParts<Self> {
81 let model = Model {
82 system_volume: 0.,
83 _service_handle: service::audio::Service::forward_local(
84 sender.input_sender().clone(),
85 Input::Audio,
86 ),
87 };
88
89 let widgets = view_output!();
90
91 ComponentParts { model, widgets }
92 }
93
94 fn update(&mut self, input: Self::Input, _sender: ComponentSender<Self>) {
95 match input {
96 Input::Scroll(delta) => {
97 let volume = self.system_volume - delta * 0.01;
98 if volume >= 0. && volume <= 1. {
99 self.system_volume = volume;
100 service::audio::Service::send(service::audio::Input::SystemVolume(
101 self.system_volume,
102 ));
103 }
104 }
105 Input::Audio(output) => match output {
106 service::audio::Output::SystemVolume(volume) => self.system_volume = volume,
107 },
108 }
109 }
110}
111
112impl Model {
113 fn system_volume_percent(&self) -> i64 {
114 (self.system_volume * 100.) as i64
115 }
116}