Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Viewer's command queue is now a channel, allowing to queue commands without mutable access #2339

Merged
merged 3 commits into from
Jun 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/re_ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ eframe = ["dep:eframe"]


[dependencies]
egui.workspace = true
crossbeam.workspace = true
egui_extras.workspace = true
egui.workspace = true
image = { workspace = true, default-features = false, features = ["png"] }
parking_lot.workspace = true
serde = { version = "1", features = ["derive"] }
Expand Down
28 changes: 16 additions & 12 deletions crates/re_ui/examples/re_ui_example.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use re_ui::{toasts, Command, CommandPalette};
use re_ui::{toasts, Command, CommandPalette, CommandReceiver, CommandSender};

fn main() -> eframe::Result<()> {
re_log::setup_native_logging();
Expand Down Expand Up @@ -49,7 +49,8 @@ pub struct ExampleApp {
cmd_palette: CommandPalette,

/// Commands to run at the end of the frame.
pending_commands: Vec<Command>,
pub command_sender: CommandSender,
command_receiver: CommandReceiver,
latest_cmd: String,
}

Expand All @@ -60,6 +61,8 @@ impl ExampleApp {

let tree = egui_tiles::Tree::new_tabs(vec![1, 2, 3]);

let (command_sender, command_receiver) = re_ui::command_channel();

Self {
re_ui,
toasts: Default::default(),
Expand All @@ -74,7 +77,8 @@ impl ExampleApp {
dummy_bool: true,

cmd_palette: CommandPalette::default(),
pending_commands: Default::default(),
command_sender,
command_receiver,
latest_cmd: Default::default(),
}
}
Expand Down Expand Up @@ -205,13 +209,13 @@ impl eframe::App for ExampleApp {
});

if let Some(cmd) = self.cmd_palette.show(egui_ctx) {
self.pending_commands.push(cmd);
self.command_sender.send(cmd);
}
if let Some(cmd) = re_ui::Command::listen_for_kb_shortcut(egui_ctx) {
self.pending_commands.push(cmd);
self.command_sender.send(cmd);
}

for cmd in self.pending_commands.drain(..) {
while let Some(cmd) = self.command_receiver.recv() {
self.latest_cmd = cmd.text().to_owned();

#[allow(clippy::single_match)]
Expand Down Expand Up @@ -248,7 +252,7 @@ impl ExampleApp {
ui.set_height(top_bar_style.height);
ui.add_space(top_bar_style.indent);

ui.menu_button("File", |ui| file_menu(ui, &mut self.pending_commands));
ui.menu_button("File", |ui| file_menu(ui, &self.command_sender));

self.top_bar_ui(ui, frame);
})
Expand Down Expand Up @@ -297,11 +301,11 @@ impl ExampleApp {
}
}

fn file_menu(ui: &mut egui::Ui, pending_commands: &mut Vec<Command>) {
Command::Save.menu_button_ui(ui, pending_commands);
Command::SaveSelection.menu_button_ui(ui, pending_commands);
Command::Open.menu_button_ui(ui, pending_commands);
Command::Quit.menu_button_ui(ui, pending_commands);
fn file_menu(ui: &mut egui::Ui, command_sender: &CommandSender) {
Command::Save.menu_button_ui(ui, command_sender);
Command::SaveSelection.menu_button_ui(ui, command_sender);
Command::Open.menu_button_ui(ui, command_sender);
Command::Quit.menu_button_ui(ui, command_sender);
}

fn selection_buttons(ui: &mut egui::Ui) {
Expand Down
33 changes: 31 additions & 2 deletions crates/re_ui/src/command.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
use egui::{Key, KeyboardShortcut, Modifiers};

/// Sender that queues up the execution of a command.
pub struct CommandSender(crossbeam::channel::Sender<Command>);

impl CommandSender {
/// Send a command to be executed.
pub fn send(&self, command: Command) {
// The only way this can fail is if the receiver has been dropped.
self.0.send(command).ok();
}
}

/// Receiver for the [`CommandSender`]
pub struct CommandReceiver(crossbeam::channel::Receiver<Command>);

impl CommandReceiver {
/// Receive a command to be executed if any is queued.
pub fn recv(&self) -> Option<Command> {
// The only way this can fail (other than being empty)
// is if the sender has been dropped.
self.0.try_recv().ok()
}
}

/// Creates a new command channel.
pub fn command_channel() -> (CommandSender, CommandReceiver) {
let (sender, receiver) = crossbeam::channel::unbounded();
(CommandSender(sender), CommandReceiver(receiver))
}

/// All the commands we support.
///
/// Most are available in the GUI,
Expand Down Expand Up @@ -232,12 +261,12 @@ impl Command {
pub fn menu_button_ui(
self,
ui: &mut egui::Ui,
pending_commands: &mut Vec<Command>,
command_sender: &CommandSender,
) -> egui::Response {
let button = self.menu_button(ui.ctx());
let response = ui.add(button).on_hover_text(self.tooltip());
if response.clicked() {
pending_commands.push(self);
command_sender.send(self);
ui.close_menu();
}
response
Expand Down
2 changes: 1 addition & 1 deletion crates/re_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mod static_image_cache;
pub mod toasts;
mod toggle_switch;

pub use command::Command;
pub use command::{command_channel, Command, CommandReceiver, CommandSender};
pub use command_palette::CommandPalette;
pub use design_tokens::DesignTokens;
pub use icons::Icon;
Expand Down
19 changes: 11 additions & 8 deletions crates/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use re_data_store::store_db::StoreDb;
use re_log_types::{ApplicationId, LogMsg, StoreId, StoreKind};
use re_renderer::WgpuResourcePoolStatistics;
use re_smart_channel::Receiver;
use re_ui::{toasts, Command};
use re_ui::{toasts, Command, CommandReceiver, CommandSender};
use re_viewer_context::{
AppOptions, ComponentUiRegistry, DynSpaceViewClass, PlayState, SpaceViewClassRegistry,
SpaceViewClassRegistryError,
Expand Down Expand Up @@ -108,7 +108,8 @@ pub struct App {
pub(crate) frame_time_history: egui::util::History<f32>,

/// Commands to run at the end of the frame.
pub(crate) pending_commands: Vec<Command>,
pub command_sender: CommandSender,
command_receiver: CommandReceiver,
cmd_palette: re_ui::CommandPalette,

analytics: ViewerAnalytics,
Expand Down Expand Up @@ -164,6 +165,8 @@ impl App {
screenshotter.screenshot_to_path_then_quit(screenshot_path);
}

let (command_sender, command_receiver) = re_ui::command_channel();

Self {
build_info,
startup_options,
Expand All @@ -188,7 +191,8 @@ impl App {

frame_time_history: egui::util::History::new(1..100, 0.5),

pending_commands: Default::default(),
command_sender,
command_receiver,
cmd_palette: Default::default(),

space_view_class_registry,
Expand Down Expand Up @@ -237,9 +241,9 @@ impl App {
self.space_view_class_registry.add::<T>()
}

fn check_keyboard_shortcuts(&mut self, egui_ctx: &egui::Context) {
fn check_keyboard_shortcuts(&self, egui_ctx: &egui::Context) {
if let Some(cmd) = Command::listen_for_kb_shortcut(egui_ctx) {
self.pending_commands.push(cmd);
self.command_sender.send(cmd);
}
}

Expand All @@ -249,8 +253,7 @@ impl App {
egui_ctx: &egui::Context,
frame: &mut eframe::Frame,
) {
let commands = self.pending_commands.drain(..).collect_vec();
for cmd in commands {
while let Some(cmd) = self.command_receiver.recv() {
self.run_command(cmd, blueprint, frame, egui_ctx);
}
}
Expand Down Expand Up @@ -906,7 +909,7 @@ impl eframe::App for App {
}

if let Some(cmd) = self.cmd_palette.show(egui_ctx) {
self.pending_commands.push(cmd);
self.command_sender.send(cmd);
}

self.run_pending_commands(&mut blueprint, egui_ctx, frame);
Expand Down
24 changes: 12 additions & 12 deletions crates/re_viewer/src/ui/rerun_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ pub fn rerun_menu_button_ui(ui: &mut egui::Ui, frame: &mut eframe::Frame, app: &

ui.add_space(spacing);

Command::ToggleCommandPalette.menu_button_ui(ui, &mut app.pending_commands);
Command::ToggleCommandPalette.menu_button_ui(ui, &app.command_sender);

ui.add_space(spacing);

#[cfg(not(target_arch = "wasm32"))]
{
Command::Open.menu_button_ui(ui, &mut app.pending_commands);
Command::Open.menu_button_ui(ui, &app.command_sender);

save_buttons_ui(ui, app);

Expand All @@ -42,24 +42,24 @@ pub fn rerun_menu_button_ui(ui: &mut egui::Ui, frame: &mut eframe::Frame, app: &
let zoom_factor = app.app_options().zoom_factor;
ui.weak(format!("Zoom {:.0}%", zoom_factor * 100.0))
.on_hover_text("The zoom factor applied on top of the OS scaling factor.");
Command::ZoomIn.menu_button_ui(ui, &mut app.pending_commands);
Command::ZoomOut.menu_button_ui(ui, &mut app.pending_commands);
Command::ZoomIn.menu_button_ui(ui, &app.command_sender);
Command::ZoomOut.menu_button_ui(ui, &app.command_sender);
ui.add_enabled_ui(zoom_factor != 1.0, |ui| {
Command::ZoomReset.menu_button_ui(ui, &mut app.pending_commands)
Command::ZoomReset.menu_button_ui(ui, &app.command_sender)
});

Command::ToggleFullscreen.menu_button_ui(ui, &mut app.pending_commands);
Command::ToggleFullscreen.menu_button_ui(ui, &app.command_sender);

ui.add_space(spacing);
}

{
Command::ResetViewer.menu_button_ui(ui, &mut app.pending_commands);
Command::ResetViewer.menu_button_ui(ui, &app.command_sender);

#[cfg(not(target_arch = "wasm32"))]
Command::OpenProfiler.menu_button_ui(ui, &mut app.pending_commands);
Command::OpenProfiler.menu_button_ui(ui, &app.command_sender);

Command::ToggleMemoryPanel.menu_button_ui(ui, &mut app.pending_commands);
Command::ToggleMemoryPanel.menu_button_ui(ui, &app.command_sender);
}

ui.add_space(spacing);
Expand All @@ -85,7 +85,7 @@ pub fn rerun_menu_button_ui(ui: &mut egui::Ui, frame: &mut eframe::Frame, app: &
#[cfg(not(target_arch = "wasm32"))]
{
ui.add_space(spacing);
Command::Quit.menu_button_ui(ui, &mut app.pending_commands);
Command::Quit.menu_button_ui(ui, &app.command_sender);
}
});
}
Expand Down Expand Up @@ -280,7 +280,7 @@ fn save_buttons_ui(ui: &mut egui::Ui, app: &mut App) {
.clicked()
{
ui.close_menu();
app.pending_commands.push(Command::Save);
app.command_sender.send(Command::Save);
}

// We need to know the loop selection _before_ we can even display the
Expand All @@ -297,7 +297,7 @@ fn save_buttons_ui(ui: &mut egui::Ui, app: &mut App) {
.clicked()
{
ui.close_menu();
app.pending_commands.push(Command::SaveSelection);
app.command_sender.send(Command::SaveSelection);
}
});
}
Expand Down
8 changes: 4 additions & 4 deletions crates/re_viewer/src/ui/top_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ fn top_bar_ui(
))
.clicked()
{
app.pending_commands.push(Command::ToggleSelectionPanel);
app.command_sender.send(Command::ToggleSelectionPanel);
}

let mut time_panel_expanded = blueprint.time_panel_expanded;
Expand All @@ -113,7 +113,7 @@ fn top_bar_ui(
))
.clicked()
{
app.pending_commands.push(Command::ToggleTimePanel);
app.command_sender.send(Command::ToggleTimePanel);
}

let mut blueprint_panel_expanded = blueprint.blueprint_panel_expanded;
Expand All @@ -130,7 +130,7 @@ fn top_bar_ui(
))
.clicked()
{
app.pending_commands.push(Command::ToggleBlueprintPanel);
app.command_sender.send(Command::ToggleBlueprintPanel);
}

if cfg!(debug_assertions) && app.app_options().show_metrics {
Expand All @@ -143,7 +143,7 @@ fn top_bar_ui(
});
}

fn frame_time_label_ui(ui: &mut egui::Ui, app: &mut App) {
fn frame_time_label_ui(ui: &mut egui::Ui, app: &App) {
if let Some(frame_time) = app.frame_time_history.average() {
let ms = frame_time * 1e3;

Expand Down