Skip to content

Commit 370e16f

Browse files
authored
Expand CommandSender to support SystemCommand (#2344)
Built on top of: #2330 Need to rebase after merging. ## Motivation The new CommandSender introduced in #2339 was limited to only supporting Commands from `re_ui`, which has a very limited set of dependencies. We needed a similar pattern for commands carrying additional data outside the context of the command palette. ## Overview - Rename `Commmand` -> `UICommand` - Introduce `SystemCommand` - Introduce a new `Command` as a union of `UICommand` and `SystemCommand` - Introduce new traits for sending the respective types. - Moves the implementation of the `CommanSender` to `re_viewer_context` so we can access it from more places. - Switches the business for setting recording id to use the new `SystemCommand` interfaces. - Adds `CommandSender` to `ViewerContext` so we can use it from the Blueprint panel in the future. ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [ ] I've included a screenshot or gif (if applicable) <!-- This line will get updated when the PR build summary job finishes. --> PR Build Summary: https://build.rerun.io/pr/2344 <!-- pr-link-docs:start --> Docs preview: https://rerun.io/preview/af3f5f8/docs Examples preview: https://rerun.io/preview/af3f5f8/examples <!-- pr-link-docs:end -->
1 parent a8acd6e commit 370e16f

File tree

15 files changed

+268
-175
lines changed

15 files changed

+268
-175
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/re_time_panel/src/time_control_ui.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ impl TimeControlUi {
213213
}
214214

215215
fn toggle_playback_text(egui_ctx: &egui::Context) -> String {
216-
if let Some(shortcut) = re_ui::Command::PlaybackTogglePlayPause.kb_shortcut() {
216+
if let Some(shortcut) = re_ui::UICommand::PlaybackTogglePlayPause.kb_shortcut() {
217217
format!(" Toggle with {}", egui_ctx.format_shortcut(&shortcut))
218218
} else {
219219
Default::default()

crates/re_ui/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ eframe = ["dep:eframe"]
2929

3030

3131
[dependencies]
32-
crossbeam.workspace = true
3332
egui_extras.workspace = true
3433
egui.workspace = true
3534
image = { workspace = true, default-features = false, features = ["png"] }

crates/re_ui/examples/re_ui_example.rs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,33 @@
1-
use re_ui::{toasts, Command, CommandPalette, CommandReceiver, CommandSender};
1+
use re_ui::{toasts, CommandPalette, UICommand, UICommandSender};
2+
3+
/// Sender that queues up the execution of a command.
4+
pub struct CommandSender(std::sync::mpsc::Sender<UICommand>);
5+
6+
impl UICommandSender for CommandSender {
7+
/// Send a command to be executed.
8+
fn send_ui(&self, command: UICommand) {
9+
// The only way this can fail is if the receiver has been dropped.
10+
self.0.send(command).ok();
11+
}
12+
}
13+
14+
/// Receiver for the [`CommandSender`]
15+
pub struct CommandReceiver(std::sync::mpsc::Receiver<UICommand>);
16+
17+
impl CommandReceiver {
18+
/// Receive a command to be executed if any is queued.
19+
pub fn recv(&self) -> Option<UICommand> {
20+
// The only way this can fail (other than being empty)
21+
// is if the sender has been dropped.
22+
self.0.try_recv().ok()
23+
}
24+
}
25+
26+
/// Creates a new command channel.
27+
fn command_channel() -> (CommandSender, CommandReceiver) {
28+
let (sender, receiver) = std::sync::mpsc::channel();
29+
(CommandSender(sender), CommandReceiver(receiver))
30+
}
231

332
fn main() -> eframe::Result<()> {
433
re_log::setup_native_logging();
@@ -61,7 +90,7 @@ impl ExampleApp {
6190

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

64-
let (command_sender, command_receiver) = re_ui::command_channel();
93+
let (command_sender, command_receiver) = command_channel();
6594

6695
Self {
6796
re_ui,
@@ -209,18 +238,18 @@ impl eframe::App for ExampleApp {
209238
});
210239

211240
if let Some(cmd) = self.cmd_palette.show(egui_ctx) {
212-
self.command_sender.send(cmd);
241+
self.command_sender.send_ui(cmd);
213242
}
214-
if let Some(cmd) = re_ui::Command::listen_for_kb_shortcut(egui_ctx) {
215-
self.command_sender.send(cmd);
243+
if let Some(cmd) = re_ui::UICommand::listen_for_kb_shortcut(egui_ctx) {
244+
self.command_sender.send_ui(cmd);
216245
}
217246

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

221250
#[allow(clippy::single_match)]
222251
match cmd {
223-
Command::ToggleCommandPalette => self.cmd_palette.toggle(),
252+
UICommand::ToggleCommandPalette => self.cmd_palette.toggle(),
224253
_ => {}
225254
}
226255
}
@@ -302,10 +331,10 @@ impl ExampleApp {
302331
}
303332

304333
fn file_menu(ui: &mut egui::Ui, command_sender: &CommandSender) {
305-
Command::Save.menu_button_ui(ui, command_sender);
306-
Command::SaveSelection.menu_button_ui(ui, command_sender);
307-
Command::Open.menu_button_ui(ui, command_sender);
308-
Command::Quit.menu_button_ui(ui, command_sender);
334+
UICommand::Save.menu_button_ui(ui, command_sender);
335+
UICommand::SaveSelection.menu_button_ui(ui, command_sender);
336+
UICommand::Open.menu_button_ui(ui, command_sender);
337+
UICommand::Quit.menu_button_ui(ui, command_sender);
309338
}
310339

311340
fn selection_buttons(ui: &mut egui::Ui) {

crates/re_ui/src/command.rs

Lines changed: 56 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,8 @@
11
use egui::{Key, KeyboardShortcut, Modifiers};
22

3-
/// Sender that queues up the execution of a command.
4-
pub struct CommandSender(crossbeam::channel::Sender<Command>);
5-
6-
impl CommandSender {
7-
/// Send a command to be executed.
8-
pub fn send(&self, command: Command) {
9-
// The only way this can fail is if the receiver has been dropped.
10-
self.0.send(command).ok();
11-
}
12-
}
13-
14-
/// Receiver for the [`CommandSender`]
15-
pub struct CommandReceiver(crossbeam::channel::Receiver<Command>);
16-
17-
impl CommandReceiver {
18-
/// Receive a command to be executed if any is queued.
19-
pub fn recv(&self) -> Option<Command> {
20-
// The only way this can fail (other than being empty)
21-
// is if the sender has been dropped.
22-
self.0.try_recv().ok()
23-
}
24-
}
25-
26-
/// Creates a new command channel.
27-
pub fn command_channel() -> (CommandSender, CommandReceiver) {
28-
let (sender, receiver) = crossbeam::channel::unbounded();
29-
(CommandSender(sender), CommandReceiver(receiver))
3+
/// Interface for sending [`UICommand`] messages.
4+
pub trait UICommandSender {
5+
fn send_ui(&self, command: UICommand);
306
}
317

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

85-
impl Command {
61+
impl UICommand {
8662
pub fn text(self) -> &'static str {
8763
self.text_and_tooltip().0
8864
}
@@ -94,76 +70,76 @@ impl Command {
9470
pub fn text_and_tooltip(self) -> (&'static str, &'static str) {
9571
match self {
9672
#[cfg(not(target_arch = "wasm32"))]
97-
Command::Save => ("Save…", "Save all data to a Rerun data file (.rrd)"),
73+
UICommand::Save => ("Save…", "Save all data to a Rerun data file (.rrd)"),
9874

9975
#[cfg(not(target_arch = "wasm32"))]
100-
Command::SaveSelection => (
76+
UICommand::SaveSelection => (
10177
"Save loop selection…",
10278
"Save data for the current loop selection to a Rerun data file (.rrd)",
10379
),
10480

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

10884
#[cfg(not(target_arch = "wasm32"))]
109-
Command::Quit => ("Quit", "Close the Rerun Viewer"),
85+
UICommand::Quit => ("Quit", "Close the Rerun Viewer"),
11086

111-
Command::ResetViewer => (
87+
UICommand::ResetViewer => (
11288
"Reset viewer",
11389
"Reset the viewer to how it looked the first time you ran it",
11490
),
11591

11692
#[cfg(not(target_arch = "wasm32"))]
117-
Command::OpenProfiler => (
93+
UICommand::OpenProfiler => (
11894
"Open profiler",
11995
"Starts a profiler, showing what makes the viewer run slow",
12096
),
12197

122-
Command::ToggleMemoryPanel => (
98+
UICommand::ToggleMemoryPanel => (
12399
"Toggle memory panel",
124100
"Investigate what is using up RAM in Rerun Viewer",
125101
),
126-
Command::ToggleBlueprintPanel => ("Toggle blueprint panel", "Toggle the left panel"),
127-
Command::ToggleSelectionPanel => ("Toggle selection panel", "Toggle the right panel"),
128-
Command::ToggleTimePanel => ("Toggle time panel", "Toggle the bottom time panel"),
102+
UICommand::ToggleBlueprintPanel => ("Toggle blueprint panel", "Toggle the left panel"),
103+
UICommand::ToggleSelectionPanel => ("Toggle selection panel", "Toggle the right panel"),
104+
UICommand::ToggleTimePanel => ("Toggle time panel", "Toggle the bottom time panel"),
129105

130106
#[cfg(not(target_arch = "wasm32"))]
131-
Command::ToggleFullscreen => (
107+
UICommand::ToggleFullscreen => (
132108
"Toggle fullscreen",
133109
"Toggle between windowed and fullscreen viewer",
134110
),
135111
#[cfg(not(target_arch = "wasm32"))]
136-
Command::ZoomIn => ("Zoom In", "Increases the ui scaling factor"),
112+
UICommand::ZoomIn => ("Zoom In", "Increases the ui scaling factor"),
137113
#[cfg(not(target_arch = "wasm32"))]
138-
Command::ZoomOut => ("Zoom Out", "Decreases the ui scaling factor"),
114+
UICommand::ZoomOut => ("Zoom Out", "Decreases the ui scaling factor"),
139115
#[cfg(not(target_arch = "wasm32"))]
140-
Command::ZoomReset => (
116+
UICommand::ZoomReset => (
141117
"Reset Zoom",
142118
"Resets ui scaling factor to the OS provided default",
143119
),
144120

145-
Command::SelectionPrevious => ("Previous selection", "Go to previous selection"),
146-
Command::SelectionNext => ("Next selection", "Go to next selection"),
147-
Command::ToggleCommandPalette => {
121+
UICommand::SelectionPrevious => ("Previous selection", "Go to previous selection"),
122+
UICommand::SelectionNext => ("Next selection", "Go to next selection"),
123+
UICommand::ToggleCommandPalette => {
148124
("Command palette…", "Toggle the command palette window")
149125
}
150126

151-
Command::PlaybackTogglePlayPause => {
127+
UICommand::PlaybackTogglePlayPause => {
152128
("Toggle play/pause", "Either play or pause the time")
153129
}
154-
Command::PlaybackFollow => ("Follow", "Follow on from end of timeline"),
155-
Command::PlaybackStepBack => (
130+
UICommand::PlaybackFollow => ("Follow", "Follow on from end of timeline"),
131+
UICommand::PlaybackStepBack => (
156132
"Step time back",
157133
"Move the time marker back to the previous point in time with any data",
158134
),
159-
Command::PlaybackStepForward => (
135+
UICommand::PlaybackStepForward => (
160136
"Step time forward",
161137
"Move the time marker to the next point in time with any data",
162138
),
163-
Command::PlaybackRestart => ("Restart", "Restart from beginning of timeline"),
139+
UICommand::PlaybackRestart => ("Restart", "Restart from beginning of timeline"),
164140

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

191167
match self {
192168
#[cfg(not(target_arch = "wasm32"))]
193-
Command::Save => Some(cmd(Key::S)),
169+
UICommand::Save => Some(cmd(Key::S)),
194170
#[cfg(not(target_arch = "wasm32"))]
195-
Command::SaveSelection => Some(cmd_shift(Key::S)),
171+
UICommand::SaveSelection => Some(cmd_shift(Key::S)),
196172
#[cfg(not(target_arch = "wasm32"))]
197-
Command::Open => Some(cmd(Key::O)),
173+
UICommand::Open => Some(cmd(Key::O)),
198174

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

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

205-
Command::ResetViewer => Some(ctrl_shift(Key::R)),
181+
UICommand::ResetViewer => Some(ctrl_shift(Key::R)),
206182
#[cfg(not(target_arch = "wasm32"))]
207-
Command::OpenProfiler => Some(ctrl_shift(Key::P)),
208-
Command::ToggleMemoryPanel => Some(ctrl_shift(Key::M)),
209-
Command::ToggleBlueprintPanel => Some(ctrl_shift(Key::B)),
210-
Command::ToggleSelectionPanel => Some(ctrl_shift(Key::S)),
211-
Command::ToggleTimePanel => Some(ctrl_shift(Key::T)),
183+
UICommand::OpenProfiler => Some(ctrl_shift(Key::P)),
184+
UICommand::ToggleMemoryPanel => Some(ctrl_shift(Key::M)),
185+
UICommand::ToggleBlueprintPanel => Some(ctrl_shift(Key::B)),
186+
UICommand::ToggleSelectionPanel => Some(ctrl_shift(Key::S)),
187+
UICommand::ToggleTimePanel => Some(ctrl_shift(Key::T)),
212188

213189
#[cfg(not(target_arch = "wasm32"))]
214-
Command::ToggleFullscreen => Some(key(Key::F11)),
190+
UICommand::ToggleFullscreen => Some(key(Key::F11)),
215191
#[cfg(not(target_arch = "wasm32"))]
216-
Command::ZoomIn => Some(egui::gui_zoom::kb_shortcuts::ZOOM_IN),
192+
UICommand::ZoomIn => Some(egui::gui_zoom::kb_shortcuts::ZOOM_IN),
217193
#[cfg(not(target_arch = "wasm32"))]
218-
Command::ZoomOut => Some(egui::gui_zoom::kb_shortcuts::ZOOM_OUT),
194+
UICommand::ZoomOut => Some(egui::gui_zoom::kb_shortcuts::ZOOM_OUT),
219195
#[cfg(not(target_arch = "wasm32"))]
220-
Command::ZoomReset => Some(egui::gui_zoom::kb_shortcuts::ZOOM_RESET),
196+
UICommand::ZoomReset => Some(egui::gui_zoom::kb_shortcuts::ZOOM_RESET),
221197

222-
Command::SelectionPrevious => Some(ctrl_shift(Key::ArrowLeft)),
223-
Command::SelectionNext => Some(ctrl_shift(Key::ArrowRight)),
224-
Command::ToggleCommandPalette => Some(cmd(Key::P)),
198+
UICommand::SelectionPrevious => Some(ctrl_shift(Key::ArrowLeft)),
199+
UICommand::SelectionNext => Some(ctrl_shift(Key::ArrowRight)),
200+
UICommand::ToggleCommandPalette => Some(cmd(Key::P)),
225201

226-
Command::PlaybackTogglePlayPause => Some(key(Key::Space)),
227-
Command::PlaybackFollow => Some(cmd(Key::ArrowRight)),
228-
Command::PlaybackStepBack => Some(key(Key::ArrowLeft)),
229-
Command::PlaybackStepForward => Some(key(Key::ArrowRight)),
230-
Command::PlaybackRestart => Some(cmd(Key::ArrowLeft)),
202+
UICommand::PlaybackTogglePlayPause => Some(key(Key::Space)),
203+
UICommand::PlaybackFollow => Some(cmd(Key::ArrowRight)),
204+
UICommand::PlaybackStepBack => Some(key(Key::ArrowLeft)),
205+
UICommand::PlaybackStepForward => Some(key(Key::ArrowRight)),
206+
UICommand::PlaybackRestart => Some(cmd(Key::ArrowLeft)),
231207

232208
#[cfg(not(target_arch = "wasm32"))]
233-
Command::ScreenshotWholeApp => None,
209+
UICommand::ScreenshotWholeApp => None,
234210
}
235211
}
236212

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

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

246222
egui_ctx.input_mut(|input| {
247-
for command in Command::iter() {
223+
for command in UICommand::iter() {
248224
if let Some(kb_shortcut) = command.kb_shortcut() {
249225
if input.consume_shortcut(&kb_shortcut) {
250226
return Some(command);
@@ -261,12 +237,12 @@ impl Command {
261237
pub fn menu_button_ui(
262238
self,
263239
ui: &mut egui::Ui,
264-
command_sender: &CommandSender,
240+
command_sender: &impl UICommandSender,
265241
) -> egui::Response {
266242
let button = self.menu_button(ui.ctx());
267243
let response = ui.add(button).on_hover_text(self.tooltip());
268244
if response.clicked() {
269-
command_sender.send(self);
245+
command_sender.send_ui(self);
270246
ui.close_menu();
271247
}
272248
response

0 commit comments

Comments
 (0)