Update
This commit is contained in:
parent
cd89d5f608
commit
11b5c806d9
@ -443,19 +443,6 @@
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label23">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Change volume on a logarithmic scale, closer to human perception.</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Normalize Volume:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="card_combo">
|
||||
<property name="visible">True</property>
|
||||
@ -477,21 +464,6 @@
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="normalize_vol_check">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
@ -533,87 +505,6 @@
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="volume_popup_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_start">12</property>
|
||||
<property name="row_spacing">6</property>
|
||||
<property name="column_spacing">12</property>
|
||||
<property name="column_homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="vol_pos_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Text Volume Position:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="vol_text_check">
|
||||
<property name="label" translatable="yes">Display Text Volume</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_vol_text_check_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="vol_pos_combo">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="active">0</property>
|
||||
<items>
|
||||
<item id="top" translatable="yes">Top</item>
|
||||
<item id="bottom" translatable="yes">Bottom</item>
|
||||
<item id="left" translatable="yes">Left</item>
|
||||
<item id="right" translatable="yes">Right</item>
|
||||
</items>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label" translatable="yes"><b>Volume Popup</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame2">
|
||||
<property name="visible">True</property>
|
||||
@ -714,7 +605,7 @@
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">1</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@ -749,7 +640,7 @@
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@ -837,18 +728,6 @@
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label32">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Fine Scroll Step:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="scroll_step_spin">
|
||||
<property name="visible">True</property>
|
||||
@ -865,22 +744,6 @@
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="fine_scroll_step_spin">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="adjustment">fine_scroll_step_adjustment</property>
|
||||
<property name="digits">2</property>
|
||||
<property name="numeric">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
|
247
src/app_state.rs
247
src/app_state.rs
@ -1,22 +1,19 @@
|
||||
use audio::Audio;
|
||||
use gdk;
|
||||
use errors::*;
|
||||
use gtk;
|
||||
use gtk::ComboBoxTextExt;
|
||||
use gtk::ComboBoxExt;
|
||||
use gtk::ToggleButtonExt;
|
||||
use gtk::SpinButtonExt;
|
||||
use gtk::ColorChooserExt;
|
||||
use gtk::EntryExt;
|
||||
use prefs::*;
|
||||
use ui_tray_icon::TrayIcon;
|
||||
use std::cell::RefCell;
|
||||
use ui_entry::Gui;
|
||||
|
||||
|
||||
// TODO: notify popups
|
||||
|
||||
|
||||
// TODO: destructors
|
||||
|
||||
// TODO: glade stuff, config, alsacard
|
||||
pub struct AppS {
|
||||
_cant_construct: (),
|
||||
pub gui: Gui,
|
||||
pub audio: Audio,
|
||||
pub prefs: RefCell<Prefs>,
|
||||
@ -33,240 +30,22 @@ impl AppS {
|
||||
Gui::new(builder_popup_window, builder_popup_menu, &prefs.borrow());
|
||||
|
||||
return AppS {
|
||||
_cant_construct: (),
|
||||
gui: gui,
|
||||
audio: Audio::new(None, Some(String::from("Master")))
|
||||
.unwrap(),
|
||||
prefs: prefs,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_tray_icon(&self) -> Result<()> {
|
||||
debug!("Update tray icon!");
|
||||
return self.gui.tray_icon.update_all(&self.prefs.borrow(), &self.audio, None);
|
||||
}
|
||||
|
||||
pub struct Gui {
|
||||
pub tray_icon: TrayIcon,
|
||||
pub popup_window: PopupWindow,
|
||||
pub popup_menu: PopupMenu,
|
||||
/* prefs_dialog is dynamically created and destroyed */
|
||||
pub prefs_dialog: RefCell<Option<PrefsDialog>>,
|
||||
}
|
||||
|
||||
|
||||
impl Gui {
|
||||
pub fn new(builder_popup_window: gtk::Builder,
|
||||
builder_popup_menu: gtk::Builder,
|
||||
prefs: &Prefs)
|
||||
-> Gui {
|
||||
return Gui {
|
||||
tray_icon: TrayIcon::new(prefs).unwrap(),
|
||||
popup_window: PopupWindow::new(builder_popup_window),
|
||||
popup_menu: PopupMenu::new(builder_popup_menu),
|
||||
prefs_dialog: RefCell::new(None),
|
||||
};
|
||||
pub fn update_popup_window(&self) -> Result<()> {
|
||||
debug!("Update PopupWindow!");
|
||||
return self.gui.popup_window.update(&self.audio);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
create_builder_item!(PopupWindow,
|
||||
popup_window: gtk::Window,
|
||||
vol_scale_adj: gtk::Adjustment,
|
||||
vol_scale: gtk::Scale,
|
||||
mute_check: gtk::CheckButton);
|
||||
|
||||
|
||||
create_builder_item!(PopupMenu,
|
||||
menu_window: gtk::Window,
|
||||
menubar: gtk::MenuBar,
|
||||
menu: gtk::Menu,
|
||||
about_item: gtk::MenuItem,
|
||||
prefs_item: gtk::MenuItem);
|
||||
|
||||
|
||||
pub struct PrefsDialog {
|
||||
pub prefs_dialog: gtk::Dialog,
|
||||
|
||||
/* DevicePrefs */
|
||||
pub card_combo: gtk::ComboBoxText,
|
||||
pub chan_combo: gtk::ComboBoxText,
|
||||
|
||||
/* ViewPrefs */
|
||||
pub vol_meter_draw_check: gtk::CheckButton,
|
||||
pub vol_meter_pos_spin: gtk::SpinButton,
|
||||
pub vol_meter_color_button: gtk::ColorButton,
|
||||
pub system_theme: gtk::CheckButton,
|
||||
|
||||
/* BehaviorPrefs */
|
||||
pub vol_control_entry: gtk::Entry,
|
||||
pub scroll_step_spin: gtk::SpinButton,
|
||||
pub middle_click_combo: gtk::ComboBoxText,
|
||||
pub custom_entry: gtk::Entry,
|
||||
|
||||
/* NotifyPrefs */
|
||||
pub noti_enable_check: gtk::CheckButton,
|
||||
pub noti_timeout_spin: gtk::SpinButton,
|
||||
// pub noti_hotkey_check: gtk::CheckButton,
|
||||
pub noti_mouse_check: gtk::CheckButton,
|
||||
pub noti_popup_check: gtk::CheckButton,
|
||||
pub noti_ext_check: gtk::CheckButton,
|
||||
}
|
||||
|
||||
impl PrefsDialog {
|
||||
pub fn new() -> PrefsDialog {
|
||||
let builder = gtk::Builder::new_from_string(include_str!("../data/ui/prefs-dialog.glade"));
|
||||
let prefs_dialog = PrefsDialog {
|
||||
prefs_dialog: builder.get_object("prefs_dialog").unwrap(),
|
||||
|
||||
card_combo: builder.get_object("card_combo").unwrap(),
|
||||
chan_combo: builder.get_object("chan_combo").unwrap(),
|
||||
|
||||
vol_meter_draw_check: builder.get_object("vol_meter_draw_check")
|
||||
.unwrap(),
|
||||
vol_meter_pos_spin: builder.get_object("vol_meter_pos_spin")
|
||||
.unwrap(),
|
||||
vol_meter_color_button: builder.get_object("vol_meter_color_button")
|
||||
.unwrap(),
|
||||
system_theme: builder.get_object("system_theme").unwrap(),
|
||||
|
||||
vol_control_entry: builder.get_object("vol_control_entry").unwrap(),
|
||||
scroll_step_spin: builder.get_object("scroll_step_spin").unwrap(),
|
||||
middle_click_combo: builder.get_object("middle_click_combo")
|
||||
.unwrap(),
|
||||
custom_entry: builder.get_object("custom_entry").unwrap(),
|
||||
|
||||
noti_enable_check: builder.get_object("noti_enable_check").unwrap(),
|
||||
noti_timeout_spin: builder.get_object("noti_timeout_spin").unwrap(),
|
||||
// noti_hotkey_check: builder.get_object("noti_hotkey_check").unwrap(),
|
||||
noti_mouse_check: builder.get_object("noti_mouse_check").unwrap(),
|
||||
noti_popup_check: builder.get_object("noti_popup_check").unwrap(),
|
||||
noti_ext_check: builder.get_object("noti_ext_check").unwrap(),
|
||||
};
|
||||
|
||||
|
||||
return prefs_dialog;
|
||||
}
|
||||
|
||||
|
||||
pub fn from_prefs(&self, prefs: &Prefs) {
|
||||
/* DevicePrefs */
|
||||
self.card_combo.remove_all();
|
||||
self.card_combo.append_text(prefs.device_prefs.card.as_str());
|
||||
self.card_combo.set_active(0);
|
||||
|
||||
self.chan_combo.remove_all();
|
||||
self.chan_combo.append_text(prefs.device_prefs.channel.as_str());
|
||||
self.chan_combo.set_active(0);
|
||||
|
||||
/* ViewPrefs */
|
||||
self.vol_meter_draw_check.set_active(prefs.view_prefs.draw_vol_meter);
|
||||
self.vol_meter_pos_spin.set_value(prefs.view_prefs.vol_meter_offset as
|
||||
f64);
|
||||
|
||||
// TODO don't convert like that
|
||||
let rgba = gdk::RGBA {
|
||||
red: prefs.view_prefs.vol_meter_color.red as f64 / 255.0,
|
||||
green: prefs.view_prefs.vol_meter_color.green as f64 / 255.0,
|
||||
blue: prefs.view_prefs.vol_meter_color.blue as f64 / 255.0,
|
||||
alpha: 1.0,
|
||||
};
|
||||
self.vol_meter_color_button.set_rgba(&rgba);
|
||||
self.system_theme.set_active(prefs.view_prefs.system_theme);
|
||||
|
||||
/* BehaviorPrefs */
|
||||
self.vol_control_entry.set_text(prefs.behavior_prefs
|
||||
.vol_control_cmd
|
||||
.as_ref()
|
||||
.unwrap_or(&String::from(""))
|
||||
.as_str());
|
||||
self.scroll_step_spin.set_value(prefs.behavior_prefs.vol_scroll_step);
|
||||
|
||||
// TODO: make sure these values always match, must be a better way
|
||||
// also check to_prefs()
|
||||
self.middle_click_combo.append_text("Toggle Mute");
|
||||
self.middle_click_combo.append_text("Show Preferences");
|
||||
self.middle_click_combo.append_text("Volume Control");
|
||||
self.middle_click_combo.append_text("Custom Command");
|
||||
self.middle_click_combo.set_active(prefs.behavior_prefs
|
||||
.middle_click_action
|
||||
.into());
|
||||
self.custom_entry.set_text(prefs.behavior_prefs
|
||||
.custom_command
|
||||
.as_ref()
|
||||
.unwrap_or(&String::from(""))
|
||||
.as_str());
|
||||
|
||||
/* NotifyPrefs */
|
||||
self.noti_enable_check
|
||||
.set_active(prefs.notify_prefs.enable_notifications);
|
||||
self.noti_timeout_spin
|
||||
.set_value(prefs.notify_prefs.notifcation_timeout as f64);
|
||||
self.noti_mouse_check
|
||||
.set_active(prefs.notify_prefs.notify_mouse_scroll);
|
||||
self.noti_popup_check.set_active(prefs.notify_prefs.notify_popup);
|
||||
self.noti_ext_check.set_active(prefs.notify_prefs.notify_external);
|
||||
}
|
||||
|
||||
|
||||
pub fn to_prefs(&self) -> Prefs {
|
||||
// TODO: remove duplication with default instance
|
||||
let device_prefs =
|
||||
DevicePrefs {
|
||||
card: self.card_combo
|
||||
.get_active_text()
|
||||
.unwrap_or(String::from("(default)")),
|
||||
channel: self.chan_combo
|
||||
.get_active_text()
|
||||
.unwrap_or(String::from("Master")),
|
||||
};
|
||||
|
||||
// TODO don't convert like that
|
||||
let vol_meter_color = VolColor {
|
||||
red: (self.vol_meter_color_button.get_rgba().red * 255.0) as u8,
|
||||
green: (self.vol_meter_color_button.get_rgba().green * 255.0) as u8,
|
||||
blue: (self.vol_meter_color_button.get_rgba().blue * 255.0) as u8,
|
||||
};
|
||||
|
||||
let view_prefs = ViewPrefs {
|
||||
draw_vol_meter: self.vol_meter_draw_check.get_active(),
|
||||
vol_meter_offset: self.vol_meter_pos_spin.get_value_as_int(),
|
||||
system_theme: self.system_theme.get_active(),
|
||||
vol_meter_color,
|
||||
};
|
||||
|
||||
let vol_control_cmd =
|
||||
self.vol_control_entry.get_text().and_then(|x| if x.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(x)
|
||||
});
|
||||
|
||||
let custom_command =
|
||||
self.custom_entry.get_text().and_then(|x| if x.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(x)
|
||||
});
|
||||
|
||||
let behavior_prefs = BehaviorPrefs {
|
||||
vol_control_cmd,
|
||||
vol_scroll_step: self.scroll_step_spin.get_value(),
|
||||
middle_click_action: self.middle_click_combo.get_active().into(),
|
||||
custom_command,
|
||||
};
|
||||
|
||||
let notify_prefs = NotifyPrefs {
|
||||
enable_notifications: self.noti_enable_check.get_active(),
|
||||
notifcation_timeout: self.noti_timeout_spin.get_value_as_int() as
|
||||
i64,
|
||||
notify_mouse_scroll: self.noti_mouse_check.get_active(),
|
||||
notify_popup: self.noti_popup_check.get_active(),
|
||||
notify_external: self.noti_ext_check.get_active(),
|
||||
};
|
||||
|
||||
return Prefs {
|
||||
device_prefs,
|
||||
view_prefs,
|
||||
behavior_prefs,
|
||||
notify_prefs,
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,40 @@
|
||||
use app_state::*;
|
||||
use audio::*;
|
||||
use std::rc::Rc;
|
||||
use gtk;
|
||||
use prefs::*;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use ui_popup_menu::*;
|
||||
use ui_popup_window::*;
|
||||
use ui_prefs_dialog::PrefsDialog;
|
||||
use ui_tray_icon::*;
|
||||
use ui_prefs_dialog::*;
|
||||
use std::ptr;
|
||||
use gtk::ResponseType;
|
||||
|
||||
|
||||
|
||||
pub struct Gui {
|
||||
_cant_construct: (),
|
||||
pub tray_icon: TrayIcon,
|
||||
pub popup_window: PopupWindow,
|
||||
pub popup_menu: PopupMenu,
|
||||
/* prefs_dialog is dynamically created and destroyed */
|
||||
pub prefs_dialog: RefCell<Option<PrefsDialog>>,
|
||||
}
|
||||
|
||||
impl Gui {
|
||||
pub fn new(builder_popup_window: gtk::Builder,
|
||||
builder_popup_menu: gtk::Builder,
|
||||
prefs: &Prefs)
|
||||
-> Gui {
|
||||
return Gui {
|
||||
_cant_construct: (),
|
||||
tray_icon: TrayIcon::new(prefs).unwrap(),
|
||||
popup_window: PopupWindow::new(builder_popup_window),
|
||||
popup_menu: PopupMenu::new(builder_popup_menu),
|
||||
prefs_dialog: RefCell::new(None),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn init(appstate: Rc<AppS>) {
|
||||
{
|
||||
let mut apps = appstate.clone();
|
||||
|
@ -1,7 +1,7 @@
|
||||
use app_state::*;
|
||||
use gtk::prelude::*;
|
||||
use std::rc::Rc;
|
||||
use gtk;
|
||||
use std::rc::Rc;
|
||||
use ui_prefs_dialog::*;
|
||||
|
||||
|
||||
@ -9,6 +9,16 @@ use ui_prefs_dialog::*;
|
||||
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
|
||||
|
||||
create_builder_item!(PopupMenu,
|
||||
menu_window: gtk::Window,
|
||||
menubar: gtk::MenuBar,
|
||||
menu: gtk::Menu,
|
||||
about_item: gtk::MenuItem,
|
||||
prefs_item: gtk::MenuItem);
|
||||
|
||||
|
||||
|
||||
pub fn init_popup_menu(appstate: Rc<AppS>) {
|
||||
|
||||
/* about_item.connect_activate_link */
|
||||
|
@ -6,14 +6,72 @@ use gdk::{GrabOwnership, GrabStatus, BUTTON_PRESS_MASK, KEY_PRESS_MASK};
|
||||
use gdk;
|
||||
use gdk_sys::{GDK_KEY_Escape, GDK_CURRENT_TIME};
|
||||
use glib;
|
||||
use gtk::ToggleButtonExt;
|
||||
use gtk::prelude::*;
|
||||
use gtk;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
||||
|
||||
pub struct PopupWindow {
|
||||
_cant_construct: (),
|
||||
pub popup_window: gtk::Window,
|
||||
pub vol_scale_adj: gtk::Adjustment,
|
||||
pub vol_scale: gtk::Scale,
|
||||
pub mute_check: gtk::CheckButton,
|
||||
pub toggle_signal: Cell<u64>,
|
||||
}
|
||||
|
||||
impl PopupWindow {
|
||||
pub fn new(builder: gtk::Builder) -> PopupWindow {
|
||||
return PopupWindow {
|
||||
_cant_construct: (),
|
||||
popup_window: builder.get_object("popup_window").unwrap(),
|
||||
vol_scale_adj: builder.get_object("vol_scale_adj").unwrap(),
|
||||
vol_scale: builder.get_object("vol_scale").unwrap(),
|
||||
mute_check: builder.get_object("mute_check").unwrap(),
|
||||
toggle_signal: Cell::new(0),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update(&self, audio: &Audio) -> Result<()> {
|
||||
let cur_vol = audio.vol()?;
|
||||
set_slider(&self.vol_scale_adj, cur_vol);
|
||||
|
||||
self.update_mute_check(&audio);
|
||||
|
||||
self.vol_scale.grab_focus();
|
||||
grab_devices(&self.popup_window)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn update_mute_check(&self, audio: &Audio) {
|
||||
let muted = audio.get_mute();
|
||||
|
||||
glib::signal_handler_block(&self.mute_check, self.toggle_signal.get());
|
||||
|
||||
match muted {
|
||||
Ok(val) => {
|
||||
self.mute_check.set_active(val);
|
||||
self.mute_check.set_tooltip_text("");
|
||||
}
|
||||
Err(_) => {
|
||||
/* can't figure out whether channel is muted, grey out */
|
||||
self.mute_check.set_active(true);
|
||||
self.mute_check.set_sensitive(false);
|
||||
self.mute_check.set_tooltip_text("Soundcard has no mute switch");
|
||||
}
|
||||
}
|
||||
|
||||
glib::signal_handler_unblock(&self.mute_check, self.toggle_signal.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn init_popup_window(appstate: Rc<AppS>) {
|
||||
let mut toggle_signal = 0;
|
||||
|
||||
/* mute_check.connect_toggled */
|
||||
{
|
||||
@ -22,10 +80,11 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
|
||||
.gui
|
||||
.popup_window
|
||||
.mute_check;
|
||||
toggle_signal =
|
||||
let toggle_signal =
|
||||
mute_check.connect_toggled(move |_| {
|
||||
on_mute_check_toggled(&_appstate)
|
||||
});
|
||||
appstate.gui.popup_window.toggle_signal.set(toggle_signal);
|
||||
}
|
||||
|
||||
/* popup_window.connect_show */
|
||||
@ -35,10 +94,8 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
|
||||
.gui
|
||||
.popup_window
|
||||
.popup_window;
|
||||
popup_window.connect_show(move |w| {
|
||||
on_popup_window_show(w,
|
||||
&_appstate,
|
||||
toggle_signal)
|
||||
popup_window.connect_show(move |_| {
|
||||
on_popup_window_show(&_appstate)
|
||||
});
|
||||
}
|
||||
|
||||
@ -56,38 +113,24 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
|
||||
|
||||
/* popup_window.connect_event */
|
||||
{
|
||||
let _appstate = appstate.clone();
|
||||
let popup_window = &appstate.clone()
|
||||
.gui
|
||||
.popup_window
|
||||
.popup_window;
|
||||
popup_window.connect_event(move |w, e| {
|
||||
on_popup_window_event(w, e, &_appstate)
|
||||
on_popup_window_event(w, e)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn on_popup_window_show(window: >k::Window,
|
||||
appstate: &AppS,
|
||||
toggle_signal: u64) {
|
||||
let audio = &appstate.audio;
|
||||
let popup_window = &appstate.gui.popup_window;
|
||||
|
||||
let cur_vol = try_w!(audio.vol());
|
||||
set_slider(&popup_window.vol_scale_adj, cur_vol);
|
||||
|
||||
let muted = audio.get_mute();
|
||||
update_mute_check(&appstate, toggle_signal, muted);
|
||||
|
||||
popup_window.vol_scale.grab_focus();
|
||||
try_w!(grab_devices(window));
|
||||
fn on_popup_window_show(appstate: &AppS) {
|
||||
try_w!(appstate.gui.popup_window.update(&appstate.audio));
|
||||
}
|
||||
|
||||
|
||||
fn on_popup_window_event(w: >k::Window,
|
||||
e: &gdk::Event,
|
||||
appstate: &AppS)
|
||||
e: &gdk::Event)
|
||||
-> gtk::Inhibit {
|
||||
match gdk::Event::get_event_type(e) {
|
||||
gdk::EventType::GrabBroken => w.hide(),
|
||||
@ -135,36 +178,12 @@ fn on_mute_check_toggled(appstate: &AppS) {
|
||||
}
|
||||
|
||||
|
||||
pub fn update_mute_check(appstate: &AppS,
|
||||
toggle_signal: u64,
|
||||
muted: Result<bool>) {
|
||||
let check_button = &appstate.gui.popup_window.mute_check;
|
||||
|
||||
glib::signal_handler_block(check_button, toggle_signal);
|
||||
|
||||
match muted {
|
||||
Ok(val) => {
|
||||
check_button.set_active(val);
|
||||
check_button.set_tooltip_text("");
|
||||
}
|
||||
Err(_) => {
|
||||
/* can't figure out whether channel is muted, grey out */
|
||||
check_button.set_active(true);
|
||||
check_button.set_sensitive(false);
|
||||
check_button.set_tooltip_text("Soundcard has no mute switch");
|
||||
}
|
||||
}
|
||||
|
||||
glib::signal_handler_unblock(check_button, toggle_signal);
|
||||
}
|
||||
|
||||
|
||||
pub fn set_slider(vol_scale_adj: >k::Adjustment, scale: f64) {
|
||||
vol_scale_adj.set_value(scale);
|
||||
}
|
||||
|
||||
|
||||
fn grab_devices(window: >k::Window) -> Result<()> {
|
||||
pub fn grab_devices(window: >k::Window) -> Result<()> {
|
||||
let device = gtk::get_current_event_device().ok_or("No current device")?;
|
||||
|
||||
let gdk_window = window.get_window().ok_or("No window?!")?;
|
||||
|
@ -1,10 +1,12 @@
|
||||
use app_state::*;
|
||||
use audio::AudioUser;
|
||||
use gdk;
|
||||
use gtk::ResponseType;
|
||||
use gtk::prelude::*;
|
||||
use gtk;
|
||||
use prefs::*;
|
||||
use std::rc::Rc;
|
||||
use support_alsa::*;
|
||||
use gtk::ResponseType;
|
||||
|
||||
|
||||
|
||||
@ -12,6 +14,199 @@ use gtk::ResponseType;
|
||||
|
||||
|
||||
|
||||
pub struct PrefsDialog {
|
||||
_cant_construct: (),
|
||||
pub prefs_dialog: gtk::Dialog,
|
||||
|
||||
/* DevicePrefs */
|
||||
pub card_combo: gtk::ComboBoxText,
|
||||
pub chan_combo: gtk::ComboBoxText,
|
||||
|
||||
/* ViewPrefs */
|
||||
pub vol_meter_draw_check: gtk::CheckButton,
|
||||
pub vol_meter_pos_spin: gtk::SpinButton,
|
||||
pub vol_meter_color_button: gtk::ColorButton,
|
||||
pub system_theme: gtk::CheckButton,
|
||||
|
||||
/* BehaviorPrefs */
|
||||
pub vol_control_entry: gtk::Entry,
|
||||
pub scroll_step_spin: gtk::SpinButton,
|
||||
pub middle_click_combo: gtk::ComboBoxText,
|
||||
pub custom_entry: gtk::Entry,
|
||||
|
||||
/* NotifyPrefs */
|
||||
pub noti_enable_check: gtk::CheckButton,
|
||||
pub noti_timeout_spin: gtk::SpinButton,
|
||||
// pub noti_hotkey_check: gtk::CheckButton,
|
||||
pub noti_mouse_check: gtk::CheckButton,
|
||||
pub noti_popup_check: gtk::CheckButton,
|
||||
pub noti_ext_check: gtk::CheckButton,
|
||||
}
|
||||
|
||||
impl PrefsDialog {
|
||||
pub fn new() -> PrefsDialog {
|
||||
let builder = gtk::Builder::new_from_string(include_str!("../data/ui/prefs-dialog.glade"));
|
||||
let prefs_dialog = PrefsDialog {
|
||||
_cant_construct: (),
|
||||
prefs_dialog: builder.get_object("prefs_dialog").unwrap(),
|
||||
|
||||
card_combo: builder.get_object("card_combo").unwrap(),
|
||||
chan_combo: builder.get_object("chan_combo").unwrap(),
|
||||
|
||||
vol_meter_draw_check: builder.get_object("vol_meter_draw_check")
|
||||
.unwrap(),
|
||||
vol_meter_pos_spin: builder.get_object("vol_meter_pos_spin")
|
||||
.unwrap(),
|
||||
vol_meter_color_button: builder.get_object("vol_meter_color_button")
|
||||
.unwrap(),
|
||||
system_theme: builder.get_object("system_theme").unwrap(),
|
||||
|
||||
vol_control_entry: builder.get_object("vol_control_entry").unwrap(),
|
||||
scroll_step_spin: builder.get_object("scroll_step_spin").unwrap(),
|
||||
middle_click_combo: builder.get_object("middle_click_combo")
|
||||
.unwrap(),
|
||||
custom_entry: builder.get_object("custom_entry").unwrap(),
|
||||
|
||||
noti_enable_check: builder.get_object("noti_enable_check").unwrap(),
|
||||
noti_timeout_spin: builder.get_object("noti_timeout_spin").unwrap(),
|
||||
// noti_hotkey_check: builder.get_object("noti_hotkey_check").unwrap(),
|
||||
noti_mouse_check: builder.get_object("noti_mouse_check").unwrap(),
|
||||
noti_popup_check: builder.get_object("noti_popup_check").unwrap(),
|
||||
noti_ext_check: builder.get_object("noti_ext_check").unwrap(),
|
||||
};
|
||||
|
||||
|
||||
return prefs_dialog;
|
||||
}
|
||||
|
||||
|
||||
pub fn from_prefs(&self, prefs: &Prefs) {
|
||||
/* DevicePrefs */
|
||||
self.card_combo.remove_all();
|
||||
self.card_combo.append_text(prefs.device_prefs.card.as_str());
|
||||
self.card_combo.set_active(0);
|
||||
|
||||
self.chan_combo.remove_all();
|
||||
self.chan_combo.append_text(prefs.device_prefs.channel.as_str());
|
||||
self.chan_combo.set_active(0);
|
||||
|
||||
/* ViewPrefs */
|
||||
self.vol_meter_draw_check.set_active(prefs.view_prefs.draw_vol_meter);
|
||||
self.vol_meter_pos_spin.set_value(prefs.view_prefs.vol_meter_offset as
|
||||
f64);
|
||||
|
||||
// TODO don't convert like that
|
||||
let rgba = gdk::RGBA {
|
||||
red: prefs.view_prefs.vol_meter_color.red as f64 / 255.0,
|
||||
green: prefs.view_prefs.vol_meter_color.green as f64 / 255.0,
|
||||
blue: prefs.view_prefs.vol_meter_color.blue as f64 / 255.0,
|
||||
alpha: 1.0,
|
||||
};
|
||||
self.vol_meter_color_button.set_rgba(&rgba);
|
||||
self.system_theme.set_active(prefs.view_prefs.system_theme);
|
||||
|
||||
/* BehaviorPrefs */
|
||||
self.vol_control_entry.set_text(prefs.behavior_prefs
|
||||
.vol_control_cmd
|
||||
.as_ref()
|
||||
.unwrap_or(&String::from(""))
|
||||
.as_str());
|
||||
self.scroll_step_spin.set_value(prefs.behavior_prefs.vol_scroll_step);
|
||||
|
||||
// TODO: make sure these values always match, must be a better way
|
||||
// also check to_prefs()
|
||||
self.middle_click_combo.append_text("Toggle Mute");
|
||||
self.middle_click_combo.append_text("Show Preferences");
|
||||
self.middle_click_combo.append_text("Volume Control");
|
||||
self.middle_click_combo.append_text("Custom Command");
|
||||
self.middle_click_combo.set_active(prefs.behavior_prefs
|
||||
.middle_click_action
|
||||
.into());
|
||||
self.custom_entry.set_text(prefs.behavior_prefs
|
||||
.custom_command
|
||||
.as_ref()
|
||||
.unwrap_or(&String::from(""))
|
||||
.as_str());
|
||||
|
||||
/* NotifyPrefs */
|
||||
self.noti_enable_check
|
||||
.set_active(prefs.notify_prefs.enable_notifications);
|
||||
self.noti_timeout_spin
|
||||
.set_value(prefs.notify_prefs.notifcation_timeout as f64);
|
||||
self.noti_mouse_check
|
||||
.set_active(prefs.notify_prefs.notify_mouse_scroll);
|
||||
self.noti_popup_check.set_active(prefs.notify_prefs.notify_popup);
|
||||
self.noti_ext_check.set_active(prefs.notify_prefs.notify_external);
|
||||
}
|
||||
|
||||
|
||||
pub fn to_prefs(&self) -> Prefs {
|
||||
// TODO: remove duplication with default instance
|
||||
let device_prefs =
|
||||
DevicePrefs {
|
||||
card: self.card_combo
|
||||
.get_active_text()
|
||||
.unwrap_or(String::from("(default)")),
|
||||
channel: self.chan_combo
|
||||
.get_active_text()
|
||||
.unwrap_or(String::from("Master")),
|
||||
};
|
||||
|
||||
// TODO don't convert like that
|
||||
let vol_meter_color = VolColor {
|
||||
red: (self.vol_meter_color_button.get_rgba().red * 255.0) as u8,
|
||||
green: (self.vol_meter_color_button.get_rgba().green * 255.0) as u8,
|
||||
blue: (self.vol_meter_color_button.get_rgba().blue * 255.0) as u8,
|
||||
};
|
||||
|
||||
let view_prefs = ViewPrefs {
|
||||
draw_vol_meter: self.vol_meter_draw_check.get_active(),
|
||||
vol_meter_offset: self.vol_meter_pos_spin.get_value_as_int(),
|
||||
system_theme: self.system_theme.get_active(),
|
||||
vol_meter_color,
|
||||
};
|
||||
|
||||
let vol_control_cmd =
|
||||
self.vol_control_entry.get_text().and_then(|x| if x.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(x)
|
||||
});
|
||||
|
||||
let custom_command =
|
||||
self.custom_entry.get_text().and_then(|x| if x.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(x)
|
||||
});
|
||||
|
||||
let behavior_prefs = BehaviorPrefs {
|
||||
vol_control_cmd,
|
||||
vol_scroll_step: self.scroll_step_spin.get_value(),
|
||||
middle_click_action: self.middle_click_combo.get_active().into(),
|
||||
custom_command,
|
||||
};
|
||||
|
||||
let notify_prefs = NotifyPrefs {
|
||||
enable_notifications: self.noti_enable_check.get_active(),
|
||||
notifcation_timeout: self.noti_timeout_spin.get_value_as_int() as
|
||||
i64,
|
||||
notify_mouse_scroll: self.noti_mouse_check.get_active(),
|
||||
notify_popup: self.noti_popup_check.get_active(),
|
||||
notify_external: self.noti_ext_check.get_active(),
|
||||
};
|
||||
|
||||
return Prefs {
|
||||
device_prefs,
|
||||
view_prefs,
|
||||
behavior_prefs,
|
||||
notify_prefs,
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn show_prefs_dialog(appstate: Rc<AppS>) {
|
||||
if appstate.gui
|
||||
.prefs_dialog
|
||||
@ -54,7 +249,6 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>) {
|
||||
let pd = m_pd.as_ref().unwrap();
|
||||
pd.prefs_dialog.connect_response(move |_, response_id| {
|
||||
|
||||
let foo = 1;
|
||||
if response_id == ResponseType::Ok.into() ||
|
||||
response_id == ResponseType::Apply.into() {
|
||||
let mut prefs = apps.prefs.borrow_mut();
|
||||
@ -72,6 +266,8 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>) {
|
||||
if response_id == ResponseType::Ok.into() ||
|
||||
response_id == ResponseType::Apply.into() {
|
||||
// TODO: update popup, tray_icon, hotkeys, notification and audio
|
||||
try_w!(apps.update_tray_icon());
|
||||
try_w!(apps.update_popup_window());
|
||||
let prefs = apps.prefs.borrow_mut();
|
||||
try_w!(prefs.store_config());
|
||||
}
|
||||
@ -79,6 +275,7 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>) {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: fix combo box behavior and filling
|
||||
/* DEVICE TAB */
|
||||
|
||||
/* card_combo.connect_changed */
|
||||
@ -194,4 +391,3 @@ fn on_chan_combo_changed(appstate: &AppS) {
|
||||
}
|
||||
|
||||
|
||||
fn prefs_dialog_to_prefs(prefs_dialog: &PrefsDialog) {}
|
||||
|
@ -13,7 +13,7 @@ use std::rc::Rc;
|
||||
use support_ui::*;
|
||||
|
||||
|
||||
// TODO: on_apply
|
||||
// TODO tooltip
|
||||
|
||||
|
||||
const ICON_MIN_SIZE: i32 = 16;
|
||||
@ -21,7 +21,8 @@ const ICON_MIN_SIZE: i32 = 16;
|
||||
|
||||
|
||||
pub struct TrayIcon {
|
||||
pub volmeter: Option<VolMeter>,
|
||||
_cant_construct: (),
|
||||
pub volmeter: RefCell<Option<VolMeter>>,
|
||||
pub audio_pix: RefCell<AudioPix>,
|
||||
pub status_icon: gtk::StatusIcon,
|
||||
pub icon_size: Cell<i32>,
|
||||
@ -34,15 +35,16 @@ impl TrayIcon {
|
||||
|
||||
let volmeter = {
|
||||
if draw_vol_meter {
|
||||
Some(VolMeter::new(prefs))
|
||||
RefCell::new(Some(VolMeter::new(prefs)))
|
||||
} else {
|
||||
None
|
||||
RefCell::new(None)
|
||||
}
|
||||
};
|
||||
let audio_pix = AudioPix::new(ICON_MIN_SIZE, prefs)?;
|
||||
let status_icon = gtk::StatusIcon::new();
|
||||
|
||||
return Ok(TrayIcon {
|
||||
_cant_construct: (),
|
||||
volmeter,
|
||||
audio_pix: RefCell::new(audio_pix),
|
||||
status_icon,
|
||||
@ -50,7 +52,33 @@ impl TrayIcon {
|
||||
});
|
||||
}
|
||||
|
||||
fn update(&self,
|
||||
|
||||
fn update_pixbuf(&self, cur_vol: f64, vol_level: VolLevel) -> Result<()> {
|
||||
let audio_pix = self.audio_pix.borrow();
|
||||
let pixbuf = audio_pix.select_pix(vol_level);
|
||||
|
||||
let vol_borrow = self.volmeter.borrow();
|
||||
let volmeter = &vol_borrow.as_ref();
|
||||
match volmeter {
|
||||
&Some(v) => {
|
||||
let vol_pix = v.meter_draw(cur_vol as i64, &pixbuf)?;
|
||||
self.status_icon.set_from_pixbuf(Some(&vol_pix));
|
||||
}
|
||||
&None => self.status_icon.set_from_pixbuf(Some(pixbuf)),
|
||||
};
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
pub fn update_audio(&self,
|
||||
audio: &Audio) -> Result<()> {
|
||||
|
||||
return self.update_pixbuf(audio.vol()?, audio.vol_level());
|
||||
}
|
||||
|
||||
|
||||
pub fn update_all(&self,
|
||||
prefs: &Prefs,
|
||||
audio: &Audio,
|
||||
m_size: Option<i32>)
|
||||
@ -63,41 +91,30 @@ impl TrayIcon {
|
||||
} else {
|
||||
self.icon_size.set(s);
|
||||
}
|
||||
/* if icon size changed, we have to re-init audio pix */
|
||||
let pixbuf = AudioPix::new(self.icon_size.get(), &prefs)?;
|
||||
*self.audio_pix.borrow_mut() = pixbuf;
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
|
||||
let cur_vol = audio.vol()?;
|
||||
let audio_pix = self.audio_pix.borrow();
|
||||
let pixbuf = audio_pix.select_pix(audio.vol_level());
|
||||
let audio_pix = AudioPix::new(self.icon_size.get(), &prefs)?;
|
||||
*self.audio_pix.borrow_mut() = audio_pix;
|
||||
let volmeter = VolMeter::new(&prefs);
|
||||
*self.volmeter.borrow_mut() = Some(volmeter);
|
||||
|
||||
let volmeter = &self.volmeter.as_ref();
|
||||
match volmeter {
|
||||
&Some(v) => {
|
||||
let vol_pix = v.meter_draw(cur_vol as i64, &pixbuf)?;
|
||||
self.status_icon.set_from_pixbuf(Some(&vol_pix));
|
||||
}
|
||||
&None => self.status_icon.set_from_pixbuf(Some(pixbuf)),
|
||||
};
|
||||
|
||||
return Ok(());
|
||||
return self.update_pixbuf(audio.vol()?, audio.vol_level());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub struct VolMeter {
|
||||
pub red: u8,
|
||||
pub green: u8,
|
||||
pub blue: u8,
|
||||
pub x_offset_pct: i64,
|
||||
pub y_offset_pct: i64,
|
||||
red: u8,
|
||||
green: u8,
|
||||
blue: u8,
|
||||
x_offset_pct: i64,
|
||||
y_offset_pct: i64,
|
||||
/* dynamic */
|
||||
pub width: Cell<i64>,
|
||||
pub row: RefCell<Vec<u8>>,
|
||||
width: Cell<i64>,
|
||||
row: RefCell<Vec<u8>>,
|
||||
}
|
||||
|
||||
|
||||
@ -280,7 +297,7 @@ impl AudioPix {
|
||||
pub fn init_tray_icon(appstate: Rc<AppS>) {
|
||||
let audio = &appstate.audio;
|
||||
let tray_icon = &appstate.gui.tray_icon;
|
||||
try_e!(tray_icon.update(&appstate.prefs.borrow_mut(), &audio, None));
|
||||
try_e!(tray_icon.update_all(&appstate.prefs.borrow_mut(), &audio, None));
|
||||
|
||||
tray_icon.status_icon.set_visible(true);
|
||||
|
||||
@ -290,10 +307,8 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
|
||||
appstate.audio.connect_handler(
|
||||
Box::new(move |s, u| match (s, u) {
|
||||
(AudioSignal::ValuesChanged, _) => {
|
||||
try_w!(apps.gui.tray_icon.update(
|
||||
&apps.prefs.borrow_mut(),
|
||||
try_w!(apps.gui.tray_icon.update_audio(
|
||||
&apps.audio,
|
||||
None,
|
||||
));
|
||||
}
|
||||
_ => (),
|
||||
@ -305,7 +320,7 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
|
||||
{
|
||||
let apps = appstate.clone();
|
||||
tray_icon.status_icon.connect_size_changed(move |_, size| {
|
||||
try_wr!(apps.gui.tray_icon.update(&apps.prefs.borrow_mut(),
|
||||
try_wr!(apps.gui.tray_icon.update_all(&apps.prefs.borrow_mut(),
|
||||
&apps.audio,
|
||||
Some(size)),
|
||||
false);
|
||||
|
Loading…
Reference in New Issue
Block a user