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

Expand CommandSender to support SystemCommand #2344

Merged
merged 5 commits into from
Jun 9, 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: 0 additions & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/re_time_panel/src/time_control_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ impl TimeControlUi {
}

fn toggle_playback_text(egui_ctx: &egui::Context) -> String {
if let Some(shortcut) = re_ui::Command::PlaybackTogglePlayPause.kb_shortcut() {
if let Some(shortcut) = re_ui::UICommand::PlaybackTogglePlayPause.kb_shortcut() {
format!(" Toggle with {}", egui_ctx.format_shortcut(&shortcut))
} else {
Default::default()
Expand Down
1 change: 0 additions & 1 deletion crates/re_ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ eframe = ["dep:eframe"]


[dependencies]
crossbeam.workspace = true
egui_extras.workspace = true
egui.workspace = true
image = { workspace = true, default-features = false, features = ["png"] }
Expand Down
49 changes: 39 additions & 10 deletions crates/re_ui/examples/re_ui_example.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,33 @@
use re_ui::{toasts, Command, CommandPalette, CommandReceiver, CommandSender};
use re_ui::{toasts, CommandPalette, UICommand, UICommandSender};

/// Sender that queues up the execution of a command.
pub struct CommandSender(std::sync::mpsc::Sender<UICommand>);

impl UICommandSender for CommandSender {
/// Send a command to be executed.
fn send_ui(&self, command: UICommand) {
// 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(std::sync::mpsc::Receiver<UICommand>);

impl CommandReceiver {
/// Receive a command to be executed if any is queued.
pub fn recv(&self) -> Option<UICommand> {
// 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.
fn command_channel() -> (CommandSender, CommandReceiver) {
let (sender, receiver) = std::sync::mpsc::channel();
(CommandSender(sender), CommandReceiver(receiver))
}

fn main() -> eframe::Result<()> {
re_log::setup_native_logging();
Expand Down Expand Up @@ -61,7 +90,7 @@ impl ExampleApp {

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

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

Self {
re_ui,
Expand Down Expand Up @@ -209,18 +238,18 @@ impl eframe::App for ExampleApp {
});

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

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

#[allow(clippy::single_match)]
match cmd {
Command::ToggleCommandPalette => self.cmd_palette.toggle(),
UICommand::ToggleCommandPalette => self.cmd_palette.toggle(),
_ => {}
}
}
Expand Down Expand Up @@ -302,10 +331,10 @@ impl ExampleApp {
}

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);
UICommand::Save.menu_button_ui(ui, command_sender);
UICommand::SaveSelection.menu_button_ui(ui, command_sender);
UICommand::Open.menu_button_ui(ui, command_sender);
UICommand::Quit.menu_button_ui(ui, command_sender);
}

fn selection_buttons(ui: &mut egui::Ui) {
Expand Down
136 changes: 56 additions & 80 deletions crates/re_ui/src/command.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,8 @@
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))
/// Interface for sending [`UICommand`] messages.
pub trait UICommandSender {
fn send_ui(&self, command: UICommand);
}

/// All the commands we support.
Expand All @@ -35,7 +11,7 @@ pub fn command_channel() -> (CommandSender, CommandReceiver) {
/// some have keyboard shortcuts,
/// and all are visible in the [`crate::CommandPalette`].
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, strum_macros::EnumIter)]
pub enum Command {
pub enum UICommand {
// Listed in the order they show up in the command palette by default!
#[cfg(not(target_arch = "wasm32"))]
Open,
Expand Down Expand Up @@ -82,7 +58,7 @@ pub enum Command {
ScreenshotWholeApp,
}

impl Command {
impl UICommand {
pub fn text(self) -> &'static str {
self.text_and_tooltip().0
}
Expand All @@ -94,76 +70,76 @@ impl Command {
pub fn text_and_tooltip(self) -> (&'static str, &'static str) {
match self {
#[cfg(not(target_arch = "wasm32"))]
Command::Save => ("Save…", "Save all data to a Rerun data file (.rrd)"),
UICommand::Save => ("Save…", "Save all data to a Rerun data file (.rrd)"),

#[cfg(not(target_arch = "wasm32"))]
Command::SaveSelection => (
UICommand::SaveSelection => (
"Save loop selection…",
"Save data for the current loop selection to a Rerun data file (.rrd)",
),

#[cfg(not(target_arch = "wasm32"))]
Command::Open => ("Open…", "Open a Rerun Data File (.rrd)"),
UICommand::Open => ("Open…", "Open a Rerun Data File (.rrd)"),

#[cfg(not(target_arch = "wasm32"))]
Command::Quit => ("Quit", "Close the Rerun Viewer"),
UICommand::Quit => ("Quit", "Close the Rerun Viewer"),

Command::ResetViewer => (
UICommand::ResetViewer => (
"Reset viewer",
"Reset the viewer to how it looked the first time you ran it",
),

#[cfg(not(target_arch = "wasm32"))]
Command::OpenProfiler => (
UICommand::OpenProfiler => (
"Open profiler",
"Starts a profiler, showing what makes the viewer run slow",
),

Command::ToggleMemoryPanel => (
UICommand::ToggleMemoryPanel => (
"Toggle memory panel",
"Investigate what is using up RAM in Rerun Viewer",
),
Command::ToggleBlueprintPanel => ("Toggle blueprint panel", "Toggle the left panel"),
Command::ToggleSelectionPanel => ("Toggle selection panel", "Toggle the right panel"),
Command::ToggleTimePanel => ("Toggle time panel", "Toggle the bottom time panel"),
UICommand::ToggleBlueprintPanel => ("Toggle blueprint panel", "Toggle the left panel"),
UICommand::ToggleSelectionPanel => ("Toggle selection panel", "Toggle the right panel"),
UICommand::ToggleTimePanel => ("Toggle time panel", "Toggle the bottom time panel"),

#[cfg(not(target_arch = "wasm32"))]
Command::ToggleFullscreen => (
UICommand::ToggleFullscreen => (
"Toggle fullscreen",
"Toggle between windowed and fullscreen viewer",
),
#[cfg(not(target_arch = "wasm32"))]
Command::ZoomIn => ("Zoom In", "Increases the ui scaling factor"),
UICommand::ZoomIn => ("Zoom In", "Increases the ui scaling factor"),
#[cfg(not(target_arch = "wasm32"))]
Command::ZoomOut => ("Zoom Out", "Decreases the ui scaling factor"),
UICommand::ZoomOut => ("Zoom Out", "Decreases the ui scaling factor"),
#[cfg(not(target_arch = "wasm32"))]
Command::ZoomReset => (
UICommand::ZoomReset => (
"Reset Zoom",
"Resets ui scaling factor to the OS provided default",
),

Command::SelectionPrevious => ("Previous selection", "Go to previous selection"),
Command::SelectionNext => ("Next selection", "Go to next selection"),
Command::ToggleCommandPalette => {
UICommand::SelectionPrevious => ("Previous selection", "Go to previous selection"),
UICommand::SelectionNext => ("Next selection", "Go to next selection"),
UICommand::ToggleCommandPalette => {
("Command palette…", "Toggle the command palette window")
}

Command::PlaybackTogglePlayPause => {
UICommand::PlaybackTogglePlayPause => {
("Toggle play/pause", "Either play or pause the time")
}
Command::PlaybackFollow => ("Follow", "Follow on from end of timeline"),
Command::PlaybackStepBack => (
UICommand::PlaybackFollow => ("Follow", "Follow on from end of timeline"),
UICommand::PlaybackStepBack => (
"Step time back",
"Move the time marker back to the previous point in time with any data",
),
Command::PlaybackStepForward => (
UICommand::PlaybackStepForward => (
"Step time forward",
"Move the time marker to the next point in time with any data",
),
Command::PlaybackRestart => ("Restart", "Restart from beginning of timeline"),
UICommand::PlaybackRestart => ("Restart", "Restart from beginning of timeline"),

#[cfg(not(target_arch = "wasm32"))]
Command::ScreenshotWholeApp => (
UICommand::ScreenshotWholeApp => (
"Screenshot",
"Copy screenshot of the whole app to clipboard",
),
Expand All @@ -190,52 +166,52 @@ impl Command {

match self {
#[cfg(not(target_arch = "wasm32"))]
Command::Save => Some(cmd(Key::S)),
UICommand::Save => Some(cmd(Key::S)),
#[cfg(not(target_arch = "wasm32"))]
Command::SaveSelection => Some(cmd_shift(Key::S)),
UICommand::SaveSelection => Some(cmd_shift(Key::S)),
#[cfg(not(target_arch = "wasm32"))]
Command::Open => Some(cmd(Key::O)),
UICommand::Open => Some(cmd(Key::O)),

#[cfg(all(not(target_arch = "wasm32"), target_os = "windows"))]
Command::Quit => Some(KeyboardShortcut::new(Modifiers::ALT, Key::F4)),
UICommand::Quit => Some(KeyboardShortcut::new(Modifiers::ALT, Key::F4)),

#[cfg(all(not(target_arch = "wasm32"), not(target_os = "windows")))]
Command::Quit => Some(cmd(Key::Q)),
UICommand::Quit => Some(cmd(Key::Q)),

Command::ResetViewer => Some(ctrl_shift(Key::R)),
UICommand::ResetViewer => Some(ctrl_shift(Key::R)),
#[cfg(not(target_arch = "wasm32"))]
Command::OpenProfiler => Some(ctrl_shift(Key::P)),
Command::ToggleMemoryPanel => Some(ctrl_shift(Key::M)),
Command::ToggleBlueprintPanel => Some(ctrl_shift(Key::B)),
Command::ToggleSelectionPanel => Some(ctrl_shift(Key::S)),
Command::ToggleTimePanel => Some(ctrl_shift(Key::T)),
UICommand::OpenProfiler => Some(ctrl_shift(Key::P)),
UICommand::ToggleMemoryPanel => Some(ctrl_shift(Key::M)),
UICommand::ToggleBlueprintPanel => Some(ctrl_shift(Key::B)),
UICommand::ToggleSelectionPanel => Some(ctrl_shift(Key::S)),
UICommand::ToggleTimePanel => Some(ctrl_shift(Key::T)),

#[cfg(not(target_arch = "wasm32"))]
Command::ToggleFullscreen => Some(key(Key::F11)),
UICommand::ToggleFullscreen => Some(key(Key::F11)),
#[cfg(not(target_arch = "wasm32"))]
Command::ZoomIn => Some(egui::gui_zoom::kb_shortcuts::ZOOM_IN),
UICommand::ZoomIn => Some(egui::gui_zoom::kb_shortcuts::ZOOM_IN),
#[cfg(not(target_arch = "wasm32"))]
Command::ZoomOut => Some(egui::gui_zoom::kb_shortcuts::ZOOM_OUT),
UICommand::ZoomOut => Some(egui::gui_zoom::kb_shortcuts::ZOOM_OUT),
#[cfg(not(target_arch = "wasm32"))]
Command::ZoomReset => Some(egui::gui_zoom::kb_shortcuts::ZOOM_RESET),
UICommand::ZoomReset => Some(egui::gui_zoom::kb_shortcuts::ZOOM_RESET),

Command::SelectionPrevious => Some(ctrl_shift(Key::ArrowLeft)),
Command::SelectionNext => Some(ctrl_shift(Key::ArrowRight)),
Command::ToggleCommandPalette => Some(cmd(Key::P)),
UICommand::SelectionPrevious => Some(ctrl_shift(Key::ArrowLeft)),
UICommand::SelectionNext => Some(ctrl_shift(Key::ArrowRight)),
UICommand::ToggleCommandPalette => Some(cmd(Key::P)),

Command::PlaybackTogglePlayPause => Some(key(Key::Space)),
Command::PlaybackFollow => Some(cmd(Key::ArrowRight)),
Command::PlaybackStepBack => Some(key(Key::ArrowLeft)),
Command::PlaybackStepForward => Some(key(Key::ArrowRight)),
Command::PlaybackRestart => Some(cmd(Key::ArrowLeft)),
UICommand::PlaybackTogglePlayPause => Some(key(Key::Space)),
UICommand::PlaybackFollow => Some(cmd(Key::ArrowRight)),
UICommand::PlaybackStepBack => Some(key(Key::ArrowLeft)),
UICommand::PlaybackStepForward => Some(key(Key::ArrowRight)),
UICommand::PlaybackRestart => Some(cmd(Key::ArrowLeft)),

#[cfg(not(target_arch = "wasm32"))]
Command::ScreenshotWholeApp => None,
UICommand::ScreenshotWholeApp => None,
}
}

#[must_use = "Returns the Command that was triggered by some keyboard shortcut"]
pub fn listen_for_kb_shortcut(egui_ctx: &egui::Context) -> Option<Command> {
pub fn listen_for_kb_shortcut(egui_ctx: &egui::Context) -> Option<UICommand> {
use strum::IntoEnumIterator as _;

let anything_has_focus = egui_ctx.memory(|mem| mem.focus().is_some());
Expand All @@ -244,7 +220,7 @@ impl Command {
}

egui_ctx.input_mut(|input| {
for command in Command::iter() {
for command in UICommand::iter() {
if let Some(kb_shortcut) = command.kb_shortcut() {
if input.consume_shortcut(&kb_shortcut) {
return Some(command);
Expand All @@ -261,12 +237,12 @@ impl Command {
pub fn menu_button_ui(
self,
ui: &mut egui::Ui,
command_sender: &CommandSender,
command_sender: &impl UICommandSender,
) -> egui::Response {
let button = self.menu_button(ui.ctx());
let response = ui.add(button).on_hover_text(self.tooltip());
if response.clicked() {
command_sender.send(self);
command_sender.send_ui(self);
ui.close_menu();
}
response
Expand Down
Loading