Skip to content
This repository has been archived by the owner on Feb 2, 2019. It is now read-only.

Commit

Permalink
ui, backend: add ctx to desc(), fix ActionList, tabbing changes, more
Browse files Browse the repository at this point in the history
- the desc() method on Actions now takes a &Context, so we can
  display the name of targeted cues.
- ActionList was fixed to actually work properly (instead of
  removing all the actions and then adding them back in, we
  call do_with_ctx! on all the UUIDs)
- We now refer to tabs by a &'static str ID, instead of an idx
  (makes things easier to refactor)
- Right-clicking on the main action list is now supported!
- The Delete key now deletes actions, as expected.
- We now show a nicer list of actions, including state, type,
  description, cue (not implemented) and duration (not implemented)
  • Loading branch information
eeeeeta committed Jun 17, 2017
1 parent 8ac2f12 commit 86b5f29
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 70 deletions.
2 changes: 1 addition & 1 deletion sqa-backend/src/actions/audio.rs
Expand Up @@ -189,7 +189,7 @@ impl EditableAction for Controller {
}
}
impl ActionController for Controller {
fn desc(&self) -> String {
fn desc(&self, ctx: &Context) -> String {
if let Some(Ok(ref url)) = self.url {
format!("Play audio at {}", url.file_name().unwrap().to_string_lossy())
}
Expand Down
12 changes: 9 additions & 3 deletions sqa-backend/src/actions/fade.rs
Expand Up @@ -8,7 +8,6 @@ use uuid::Uuid;
use std::time::Duration;
use std::default::Default;
use super::audio::db_lin;

#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct FadeParams {
pub target: Option<Uuid>,
Expand Down Expand Up @@ -36,8 +35,15 @@ impl EditableAction for Controller {
}
}
impl ActionController for Controller {
fn desc(&self) -> String {
format!("Fade action {:?}", self.params)
fn desc(&self, ctx: &Context) -> String {
if let Some(tgt) = self.params.target.as_ref() {
if let Some(tgt) = ctx.actions.get(tgt) {
if let ActionType::Audio(ref ctl) = tgt.ctl {
return format!("Fade {}", tgt.meta.name.as_ref().unwrap_or(&ctl.desc(ctx)));
}
}
}
format!("Fade [invalid]")
}
fn verify_params(&self, ctx: &mut Context) -> Vec<ParameterError> {
let mut ret = vec![];
Expand Down
51 changes: 27 additions & 24 deletions sqa-backend/src/actions/mod.rs
Expand Up @@ -48,7 +48,7 @@ pub trait EditableAction {
fn set_params(&mut self, Self::Parameters, ctx: &mut Context);
}
pub trait ActionController {
fn desc(&self) -> String;
fn desc(&self, ctx: &Context) -> String;
fn verify_params(&self, ctx: &mut Context) -> Vec<ParameterError>;
fn load(&mut self, _ctx: ControllerParams) -> BackendResult<bool> {
Ok(true)
Expand Down Expand Up @@ -77,28 +77,31 @@ pub enum ActionParameters {
Audio(<audio::Controller as EditableAction>::Parameters),
Fade(<fade::Controller as EditableAction>::Parameters)
}
macro_rules! action {
(mut $a:expr) => {{
use self::ActionType::*;
match $a {
Audio(ref mut a) => a as &mut ActionController,
Fade(ref mut a) => a as &mut ActionController
}
}};
($a:expr) => {{
use self::ActionType::*;
match $a {
Audio(ref a) => a as &ActionController,
Fade(ref a) => a as &ActionController,
}
}};
(params $a:expr) => {{
use self::ActionType::*;
match $a {
Audio(ref a) => ActionParameters::Audio(a.get_params().clone()),
Fade(ref a) => ActionParameters::Fade(a.get_params().clone())
}
}};
#[macro_use]
pub mod macros {
macro_rules! action {
(mut $a:expr) => {{
use self::ActionType::*;
match $a {
Audio(ref mut a) => a as &mut ActionController,
Fade(ref mut a) => a as &mut ActionController
}
}};
($a:expr) => {{
use self::ActionType::*;
match $a {
Audio(ref a) => a as &ActionController,
Fade(ref a) => a as &ActionController,
}
}};
(params $a:expr) => {{
use self::ActionType::*;
match $a {
Audio(ref a) => ActionParameters::Audio(a.get_params().clone()),
Fade(ref a) => ActionParameters::Fade(a.get_params().clone())
}
}};
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct OpaqueAction {
Expand Down Expand Up @@ -216,7 +219,7 @@ impl Action {
params: action!(params self.ctl),
uu: self.uu,
meta: self.meta.clone(),
desc: action!(self.ctl).desc()
desc: action!(self.ctl).desc(ctx)
})
}
pub fn verify_params(&mut self, ctx: &mut Context) {
Expand Down
19 changes: 11 additions & 8 deletions sqa-backend/src/state.rs
Expand Up @@ -152,14 +152,17 @@ impl ConnHandler for Context {
},
ActionList => {
let mut resp = HashMap::new();
for (uu, mut act) in mem::replace(&mut self.actions, HashMap::new()).into_iter() {
if let Ok(data) = act.get_data(self) {
resp.insert(uu, data);
}
else {
println!("FIXME: handle failure to get_data");
}
self.actions.insert(uu, act);
let uus = self.actions.iter().map(|(x, _)| x.clone()).collect::<Vec<_>>();
for uu in uus {
let _: Result<(), String> = do_with_ctx!(self, &uu, |a: &mut Action| {
if let Ok(data) = a.get_data(self) {
resp.insert(uu, data);
}
else {
println!("FIXME: handle failure to get_data");
}
Ok(())
});
}
d.respond(ReplyActionList { list: resp })?;
},
Expand Down
10 changes: 5 additions & 5 deletions sqa-ui/src/actions/audio.rs
Expand Up @@ -39,9 +39,8 @@ impl AudioUI {
let params = Default::default();
let cnf = Default::default();
let sb = SliderBox::new(0, 0, &tx, uu);
temp.notebk_tabs[0].append_property("File target", &file);
let patch = temp.add_tab();
patch.label.set_markup("Levels &amp; Patch");
temp.get_tab("Basics").append_property("File target", &file);
let patch = temp.add_tab("Levels &amp; Patch");
patch.container.pack_start(&sb.grid, false, true, 5);
let mut ctx = AudioUI { file, temp, params, cnf, sb };
ctx.bind();
Expand All @@ -68,8 +67,9 @@ impl AudioUI {
trace!("audio: recreating sliders!");
self.sb.grid.destroy();
self.sb = SliderBox::new(p.chans.len(), self.cnf.defs.len(), &self.temp.tx, self.temp.uu);
self.temp.notebk_tabs[1].container.pack_start(&self.sb.grid, false, true, 5);
self.temp.notebk_tabs[1].container.show_all();
let tab = self.temp.get_tab("Levels &amp; Patch");
tab.container.pack_start(&self.sb.grid, false, true, 5);
tab.container.show_all();
}
let mut details = p.chans.iter()
.map(|ch| {
Expand Down
18 changes: 10 additions & 8 deletions sqa-ui/src/actions/fade.rs
@@ -1,5 +1,5 @@
use gtk::prelude::*;
use gtk::{Button, Widget};
use gtk::{Button, Widget, Image, Align};
use super::{ActionMessageInner, ActionInternalMessage, ActionMessage, OpaqueAction, UISender, ActionUI, UITemplate};
use std::time::Duration;
use widgets::{SliderBox, Faded, DurationEntry, DurationEntryMessage, SliderMessage, FadedSliderMessage};
Expand Down Expand Up @@ -55,13 +55,14 @@ impl FadeUI {
let sel = Button::new_with_label("[choose...]");
let selecting = Rc::new(Cell::new(false));
let actionlist = HashMap::new();
let patch = temp.add_tab();
let fade = temp.add_tab();
patch.label.set_markup("Levels &amp; Patch");
fade.label.set_markup("Fade Properties");
temp.notebk_tabs[0].append_property("Target", &sel);
let patch = temp.add_tab("Levels &amp; Patch");
let fade = temp.add_tab("Fade Properties");
temp.get_tab("Basics").append_property("Target", &sel);
fade.append_property("Duration", &*dur);
patch.container.pack_start(&sb.grid, false, true, 5);
sel.set_halign(Align::Start);
sel.set_always_show_image(true);
sel.set_image(&Image::new_from_stock("gtk-find", 4));
let mut ctx = FadeUI { temp, params, sb, sel, tx, selecting, actionlist, dur };
ctx.bind();
ctx
Expand Down Expand Up @@ -101,8 +102,9 @@ impl FadeUI {
if p.fades.len() != self.sb.n_sliders() {
self.sb.grid.destroy();
self.sb = SliderBox::new(p.fades.len(), 0, &self.temp.tx, self.temp.uu);
self.temp.notebk_tabs[1].container.pack_start(&self.sb.grid, false, true, 5);
self.temp.notebk_tabs[1].container.show_all();
let tab = self.temp.get_tab("Levels &amp; Patch");
tab.container.pack_start(&self.sb.grid, false, true, 5);
tab.container.show_all();
}
let mut fades = p.fades.clone();
fades.insert(0, p.fade_master.clone());
Expand Down
46 changes: 40 additions & 6 deletions sqa-ui/src/actions/mod.rs
@@ -1,13 +1,13 @@
use gtk::prelude::*;
use gtk::{self, Widget, TreeView, ListStore, Builder, MenuItem, TreeSelection, TargetEntry, TargetFlags, Stack};
use gtk::{self, Widget, Menu, TreeView, ListStore, Builder, MenuItem, TreeSelection, TargetEntry, TargetFlags, Stack};
use gdk;
use uuid::Uuid;
use std::collections::HashMap;
use std::mem;
use sync::UISender;
use sqa_backend::codec::{Command, Reply};
use sqa_backend::mixer::MixerConf;
use sqa_backend::actions::{ActionParameters, OpaqueAction};
use sqa_backend::actions::{ActionParameters, PlaybackState, OpaqueAction};
use sqa_backend::actions::audio::AudioParams;
use messages::Message;

Expand Down Expand Up @@ -52,6 +52,7 @@ pub struct ActionController {
ctls: HashMap<Uuid, Box<ActionUI>>,
opas: HashMap<Uuid, OpaqueAction>,
cur_sel: Option<SelectionDetails>,
menu: Menu,
medit: MenuItem,
mdelete: MenuItem,
mload: MenuItem,
Expand Down Expand Up @@ -124,7 +125,7 @@ impl ActionController {
let mixer = Default::default();
build!(ActionController using b
with ctls, opas, tx, mixer, cur_widget, cur_sel, cur_page
get view, store, medit, mload, mexec, mdelete, mcreate_audio, mcreate_fade, sidebar)
get view, store, menu, medit, mload, mexec, mdelete, mcreate_audio, mcreate_fade, sidebar)
}
pub fn bind(&mut self, tx: &UISender) {
use self::ActionMessageInner::*;
Expand All @@ -137,6 +138,15 @@ impl ActionController {
mload => LoadAction,
mdelete => DeleteAction
}
let menu = self.menu.clone();
self.view.connect_button_press_event(move |_, eb| {
if eb.get_button() == 3 {
if let ::gdk::EventType::ButtonPress = eb.get_event_type() {
menu.popup_easy(eb.get_button(), eb.get_time());
}
}
Inhibit(false)
});
self.view.get_selection().connect_changed(clone!(tx; |_| {
tx.send_internal(ActionInternalMessage::SelectionChanged);
}));
Expand All @@ -163,9 +173,30 @@ impl ActionController {
let sel = if sel.is_some() { sel } else { tsg.get() };
self.store.clear();
for (uu, action) in self.opas.iter() {
let iter = self.store.insert_with_values(None, &[0, 1], &[
let typ = match action.params {
ActionParameters::Audio(_) => "audio-x-generic",
ActionParameters::Fade(_) => "audio-volume-medium-symbolic"
};
use self::PlaybackState::*;
let state = match action.state {
Inactive => "",
Unverified(_) => "gtk-dialog-warning",
Loaded => "gtk-home",
Loading => "gtk-refresh",
Paused => "gtk-media-pause",
Active(_) => "gtk-media-play",
Errored(_) => "gtk-dialog-error"
};
let iter = self.store.insert_with_values(None, &[
0, // uuid
1, // description
2, // icon-state (playback state icon)
3, // icon-type (action type icon)
], &[
&uu.to_string(),
&action.display_name()
&action.display_name(),
&state,
&typ
]);
if let Some(u2) = sel {
if *uu == u2 {
Expand Down Expand Up @@ -219,7 +250,10 @@ impl ActionController {
action_reply_notify!(self, res, "Loading action", "Action loaded.");
},
ActionParamsUpdated { res, .. } => {
action_reply_notify!(self, res, "Modifying action", "Action modified.");
if let Err(e) = res {
let msg = Message::Error(format!("Modifying action failed: {}", e));
self.tx.as_mut().unwrap().send_internal(msg);
}
},
ActionExecuted { res, .. } => {
action_reply_notify!(self, res, "Executing action", "Action executed.");
Expand Down
43 changes: 36 additions & 7 deletions sqa-ui/src/actions/template.rs
@@ -1,6 +1,7 @@
use gtk::prelude::*;
use gtk::{Button, ButtonBox, ButtonBoxStyle, Box, Label, Orientation, Notebook, Widget, ScrolledWindow, Entry};
use gtk::{Button, ButtonBox, ButtonBoxStyle, Box, Label, Image, Orientation, Notebook, Widget, ScrolledWindow, Entry, ListBox, SelectionMode};
use widgets::PropertyWindow;
use std::collections::HashMap;
use sync::UISender;
use uuid::Uuid;
use sqa_backend::actions::{PlaybackState, OpaqueAction};
Expand All @@ -26,13 +27,14 @@ impl ActionTab {
pub struct UITemplate {
pub pwin: PropertyWindow,
pub notebk: Notebook,
pub notebk_tabs: Vec<ActionTab>,
pub notebk_tabs: HashMap<&'static str, ActionTab>,
pub close_btn: Button,
pub load_btn: Button,
pub execute_btn: Button,
pub name_ent: Entry,
pub tx: UISender,
pub popped_out: bool,
pub errors_list: ListBox,
pub uu: Uuid
}

Expand All @@ -44,8 +46,9 @@ impl UITemplate {
load_btn: Button::new_with_mnemonic("_Load"),
execute_btn: Button::new_with_mnemonic("_Execute"),
notebk: Notebook::new(),
notebk_tabs: Vec::new(),
notebk_tabs: HashMap::new(),
name_ent: Entry::new(),
errors_list: ListBox::new(),
popped_out: false,
tx, uu
};
Expand All @@ -54,25 +57,33 @@ impl UITemplate {
btn_box.pack_start(&ret.load_btn, false, false, 0);
btn_box.pack_start(&ret.execute_btn, false, false, 0);
ret.pwin.append_button(&ret.close_btn);
let basics_tab = ret.add_tab();
basics_tab.label.set_markup("Basics");
let basics_tab = ret.add_tab("Basics");
let errors_tab = ret.add_tab("Errors");
basics_tab.container.pack_start(&btn_box, false, false, 0);
basics_tab.append_property("Name", &ret.name_ent);
errors_tab.container.pack_start(&ret.errors_list, false, false, 0);
ret.errors_list.set_selection_mode(SelectionMode::None);
ret.pwin.props_box.pack_start(&ret.notebk, true, true, 0);
ret
}
pub fn add_tab(&mut self) -> ActionTab {
pub fn add_tab(&mut self, id: &'static str) -> ActionTab {
let bx = Box::new(Orientation::Vertical, 0);
bx.set_margin_left(5);
bx.set_margin_right(5);
bx.set_margin_top(5);
bx.set_margin_bottom(5);
let lbl = Label::new(None);
lbl.set_markup(id);
let at = ActionTab { container: bx, label: lbl };
self.notebk.insert_page(&at.container, Some(&at.label), None);
self.notebk_tabs.push(at.clone());
self.notebk_tabs.insert(id, at.clone());
trace!("inserting tab '{}' for act {}", id, self.uu);
at
}
pub fn get_tab(&self, id: &'static str) -> &ActionTab {
trace!("getting tab '{}' for act {}", id, self.uu);
self.notebk_tabs.get(id).expect(&format!("failed to get tab '{}' for act {}", id, self.uu))
}
pub fn get_container(&mut self) -> Option<Widget> {
if self.pwin.window.is_visible() {
None
Expand Down Expand Up @@ -127,6 +138,24 @@ impl UITemplate {
playback_state_update(p, &mut self.pwin);
self.name_ent.set_placeholder_text(&p.desc as &str);
self.name_ent.set_text(p.meta.name.as_ref().map(|s| s as &str).unwrap_or(""));
for child in self.errors_list.get_children() {
self.errors_list.remove(&child);
}
if let PlaybackState::Unverified(ref errs) = p.state {
self.get_tab("Errors").label.set_markup(&format!("Errors ({})", errs.len()));
for err in errs {
let bx = Box::new(Orientation::Horizontal, 5);
let img = Image::new_from_icon_name("gtk-dialog-error", 4);
let lbl = Label::new(None);
lbl.set_markup(&format!("{}: {}", err.name, err.err));
bx.pack_start(&img, false, false, 0);
bx.pack_start(&lbl, false, false, 0);
self.errors_list.add(&bx);
}
}
else {
self.get_tab("Errors").label.set_markup("Errors");
}
}
}
pub fn playback_state_update(p: &OpaqueAction, pwin: &mut PropertyWindow) {
Expand Down

0 comments on commit 86b5f29

Please sign in to comment.