Skip to content

Commit

Permalink
Playtime: Just hide app when closing window, don't actually close it
Browse files Browse the repository at this point in the history
  • Loading branch information
helgoboss committed Oct 25, 2023
1 parent 4343e5c commit 18c8da1
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 21 deletions.
75 changes: 64 additions & 11 deletions main/src/infrastructure/ui/app/app_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use anyhow::{anyhow, Context, Result};
use base::Global;
use c_str_macro::c_str;
use playtime_clip_engine::proto;
use playtime_clip_engine::proto::{reply, ClipEngineReceivers, EventReply, Reply};
use playtime_clip_engine::proto::{
event_reply, occasional_matrix_update, reply, ClipEngineReceivers, Empty, EventReply,
GetOccasionalMatrixUpdatesReply, OccasionalMatrixUpdate, Reply,
};
use prost::Message;
use reaper_low::{raw, Swell};
use std::cell::RefCell;
Expand All @@ -28,7 +31,7 @@ struct OpenState {
app_handle: AppHandle,
app_callback: Option<AppCallback>,
// TODO-medium This is too specific.
event_receivers: ClipEngineReceivers,
event_receivers: Option<ClipEngineReceivers>,
}

impl AppPanel {
Expand Down Expand Up @@ -65,11 +68,19 @@ impl AppPanel {
// Handshake finished! The app has the host callback and we have the app callback.
open_state.app_callback = Some(callback);
// Now we can start passing events to the app callback
self.start_timer();
}

fn start_timer(&self) {
self.view
.require_window()
.set_timer(TIMER_ID, Duration::from_millis(30));
}

fn stop_timer(&self) {
self.view.require_window().kill_timer(TIMER_ID);
}

fn open_internal(&self, window: Window) -> Result<()> {
let app_library = App::get_or_load_app_library()?;
let session_id = self
Expand All @@ -83,7 +94,7 @@ impl AppPanel {
let open_state = OpenState {
app_handle,
app_callback: None,
event_receivers: App::get().clip_engine_hub().senders().subscribe_to_all(),
event_receivers: Some(subscribe_to_events()),
};
*self.open_state.borrow_mut() = Some(open_state);
Ok(())
Expand All @@ -107,16 +118,15 @@ impl OpenState {
}

pub fn send_pending_events(&mut self, session_id: &str) {
let Some(app_callback) = self.app_callback else {
let (Some(app_callback), Some(event_receivers)) = (self.app_callback, &mut self.event_receivers) else {
return;
};
self.event_receivers
.process_pending_updates(session_id, &|event_reply| {
let reply = Reply {
value: Some(reply::Value::EventReply(event_reply)),
};
let _ = send_to_app(app_callback, &reply);
});
event_receivers.process_pending_updates(session_id, &|event_reply| {
let reply = Reply {
value: Some(reply::Value::EventReply(event_reply)),
};
let _ = send_to_app(app_callback, &reply);
});
}

pub fn close_app(&self, window: Window) -> Result<()> {
Expand All @@ -137,10 +147,49 @@ impl View for AppPanel {
self.open_internal(window).is_ok()
}

fn close_requested(self: SharedView<Self>) -> bool {
// Don't really close window (along with the app instance). Just hide it. It's a bit faster
// when next opening the window.
self.view.require_window().hide();
true
}

fn closed(self: SharedView<Self>, window: Window) {
self.close_internal(window).unwrap();
}

fn shown_or_hidden(self: SharedView<Self>, shown: bool) -> bool {
if shown {
// Send events to app again.
if let Some(open_state) = self.open_state.borrow_mut().as_mut() {
open_state.event_receivers = Some(subscribe_to_events());
} else {
// We also get called when the window is first opened, *before* `opened` is called!
// In that case, `open_state` is not set yet. That's how we know it's the first opening,
// not a subsequent show. We don't need to do anything in that case.
return false;
}
// Start processing events again when shown
self.start_timer();
// Send a reset event to the app. That's not necessary when the app is first
// shown because it resubscribes to everything on start anyway. But it's important for
// subsequent shows because the app was not aware that it was not fed with events while
// hidden.
let _ = self.send_to_app(&Reply {
value: Some(reply::Value::EventReply(EventReply {
value: Some(event_reply::Value::Reset(Empty {})),
})),
});
} else {
// Don't process events while hidden
if let Some(open_state) = self.open_state.borrow_mut().as_mut() {
open_state.event_receivers = None;
}
self.stop_timer();
}
true
}

#[allow(clippy::single_match)]
fn button_clicked(self: SharedView<Self>, resource_id: u32) {
match resource_id {
Expand Down Expand Up @@ -212,3 +261,7 @@ fn send_to_app(app_callback: AppCallback, reply: &Reply) {

/// Signature of the function that's used from the host in order to call the external app.
pub type AppCallback = unsafe extern "C" fn(data: *const u8, length: i32);

fn subscribe_to_events() -> ClipEngineReceivers {
App::get().clip_engine_hub().senders().subscribe_to_all()
}
28 changes: 21 additions & 7 deletions main/src/infrastructure/ui/header_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ pub struct HeaderPanel {
group_panel: RefCell<Option<SharedView<GroupPanel>>>,
extra_panel: RefCell<Option<SharedView<dyn View>>>,
pot_browser_panel: RefCell<Option<SharedView<dyn View>>>,
app_panel: RefCell<Option<SharedView<dyn View>>>,
is_invoked_programmatically: Cell<bool>,
}

Expand All @@ -101,7 +100,6 @@ impl HeaderPanel {
group_panel: Default::default(),
extra_panel: Default::default(),
pot_browser_panel: Default::default(),
app_panel: Default::default(),
is_invoked_programmatically: false.into(),
}
}
Expand Down Expand Up @@ -141,7 +139,6 @@ impl HeaderPanel {
close_child_panel_if_open(&self.group_panel);
close_child_panel_if_open(&self.extra_panel);
close_child_panel_if_open(&self.pot_browser_panel);
close_child_panel_if_open(&self.app_panel);
}

pub fn handle_changed_midi_devices(&self) {
Expand Down Expand Up @@ -284,6 +281,7 @@ impl HeaderPanel {
let main_preset_manager = main_preset_manager.borrow();
let text_from_clipboard = Rc::new(get_text_from_clipboard().unwrap_or_default());
let text_from_clipboard_clone = text_from_clipboard.clone();
let app_is_open = self.panel_manager().borrow().app_panel_is_open();
let data_object_from_clipboard = if text_from_clipboard.is_empty() {
None
} else {
Expand Down Expand Up @@ -589,7 +587,15 @@ impl HeaderPanel {
MainMenuAction::ReloadAllPresets
}),
item("Open Pot Browser", || MainMenuAction::OpenPotBrowser),
item("Open App", || MainMenuAction::OpenApp),
item("Show App", || MainMenuAction::ShowApp),
item_with_opts(
"Close App",
ItemOpts {
enabled: app_is_open,
checked: false,
},
|| MainMenuAction::CloseApp,
),
separator(),
menu(
"Logging",
Expand Down Expand Up @@ -754,9 +760,12 @@ impl HeaderPanel {
MainMenuAction::OpenPotBrowser => {
self.show_pot_browser();
}
MainMenuAction::OpenApp => {
MainMenuAction::ShowApp => {
self.show_app();
}
MainMenuAction::CloseApp => {
self.close_app();
}
MainMenuAction::OpenPresetFolder => self.open_preset_folder(),
MainMenuAction::SendFeedbackNow => self.session().borrow().send_all_feedback(),
MainMenuAction::LogDebugInfo => self.log_debug_info(),
Expand Down Expand Up @@ -2287,7 +2296,11 @@ impl HeaderPanel {
}

fn show_app(&self) {
self.panel_manager().borrow().open_app_panel();
self.panel_manager().borrow().show_app_panel();
}

fn close_app(&self) {
self.panel_manager().borrow().close_app_panel();
}

fn open_preset_folder(&self) {
Expand Down Expand Up @@ -3001,7 +3014,8 @@ enum MainMenuAction {
LinkToPreset(PresetLinkScope, FxId, String),
ReloadAllPresets,
OpenPotBrowser,
OpenApp,
ShowApp,
CloseApp,
OpenPresetFolder,
EditNewOscDevice,
EditExistingOscDevice(OscDeviceId),
Expand Down
20 changes: 17 additions & 3 deletions main/src/infrastructure/ui/independent_panel_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,25 @@ impl IndependentPanelManager {
self.message_panel.clone().open(reaper_main_window());
}

pub fn open_app_panel(&self) {
let result = self.open_app_panel_internal();
pub fn show_app_panel(&self) {
let result = self.show_app_panel_internal();
notification::notify_user_on_anyhow_error(result);
}

fn open_app_panel_internal(&self) -> anyhow::Result<()> {
pub fn close_app_panel(&self) {
self.app_panel.clone().close();
}

pub fn app_panel_is_open(&self) -> bool {
self.app_panel.is_open()
}

fn show_app_panel_internal(&self) -> anyhow::Result<()> {
if let Some(window) = self.app_panel.view_context().window() {
// If window already open (and maybe just hidden), simply show it.
window.show();
return Ok(());
}
// Fail fast if library not available
let _ = App::get_or_load_app_library()?;
// Then open
Expand Down Expand Up @@ -154,6 +167,7 @@ impl IndependentPanelManager {
p.close()
}
self.mapping_panels.clear();
self.app_panel.close();
}

fn request_panel(&mut self) -> SharedView<MappingPanel> {
Expand Down
7 changes: 7 additions & 0 deletions swell-ui/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ pub trait View: Debug {
/// WM_DESTROY.
fn closed(self: SharedView<Self>, _window: Window) {}

/// WM_SHOWWINDOW.
///
/// Should return `true` if processed.
fn shown_or_hidden(self: SharedView<Self>, shown: bool) -> bool {
false
}

/// WM_COMMAND, HIWORD(wparam) == 0.
fn button_clicked(self: SharedView<Self>, resource_id: u32) {
let _ = resource_id;
Expand Down
1 change: 1 addition & 0 deletions swell-ui/src/view_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ unsafe extern "C" fn view_dialog_proc(
// `SetWindowLong()` for return values with special meaning.
keyboard_focus_desired.into()
}
raw::WM_SHOWWINDOW => view.shown_or_hidden(wparam == 1).into(),
raw::WM_DESTROY => {
let view_context = view.view_context();
view_context.closed_subject.borrow_mut().next(());
Expand Down

0 comments on commit 18c8da1

Please sign in to comment.