This commit is contained in:
Ospald, Julian 2017-07-07 12:03:53 +02:00
parent cd89d5f608
commit 11b5c806d9
7 changed files with 370 additions and 464 deletions

View File

@ -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">&lt;b&gt;Volume Popup&lt;/b&gt;</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">

View File

@ -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,
};
}
}

View File

@ -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();

View File

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

View File

@ -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: &gtk::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: &gtk::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: &gtk::Adjustment, scale: f64) {
vol_scale_adj.set_value(scale);
}
fn grab_devices(window: &gtk::Window) -> Result<()> {
pub fn grab_devices(window: &gtk::Window) -> Result<()> {
let device = gtk::get_current_event_device().ok_or("No current device")?;
let gdk_window = window.get_window().ok_or("No window?!")?;

View File

@ -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) {}

View File

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