-
Notifications
You must be signed in to change notification settings - Fork 251
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
re_ui
crate to encapsulate our own egui theme
#476
Merged
Changes from 9 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
b62eafa
Create re_ui crate
emilk 4368352
Move design tokens into re_ui
emilk 60b5c53
Move StaticImageCache into re_ui
emilk 9a940e5
Create ReUi wrapper around DesignTokens and StaticImageCache
emilk adb16a1
Move watermark
emilk 614231d
Refactor Top Bar logic
emilk 1abdd65
Move icons and medium_icon_toggle_button
emilk ea42353
cargo fmt
emilk a2de0dc
Update to latest egui
emilk 150873d
Remove glutin dep
emilk 2d5e203
Add top-level docs to re_ui
emilk ff2add1
Fix clamping of event log
emilk File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
[package] | ||
name = "re_ui" | ||
version.workspace = true | ||
edition.workspace = true | ||
rust-version.workspace = true | ||
license = "(MIT OR Apache-2.0) AND OFL-1.1" | ||
publish = false | ||
include = [ | ||
"../../LICENSE-APACHE", | ||
"../../LICENSE-MIT", | ||
"**/*.rs", | ||
"Cargo.toml", | ||
"data/*", | ||
] | ||
|
||
|
||
[features] | ||
default = [] | ||
|
||
|
||
[dependencies] | ||
egui = { version = "0.19", features = ["extra_debug_asserts", "tracing"] } | ||
egui_extras = { version = "0.19", features = ["tracing"] } | ||
image = { version = "0.24", default-features = false, features = ["png"] } | ||
parking_lot = "0.12" | ||
serde = { version = "1", features = ["derive"] } | ||
serde_json = "1" | ||
|
||
|
||
[dev-dependencies] | ||
eframe = { version = "0.19", default-features = false, features = ["wgpu"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Rerun GUI theme and helpers, built around [`egui`](https://www.egui.rs/). |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
fn main() { | ||
let native_options = eframe::NativeOptions { | ||
initial_window_size: Some([1200.0, 800.0].into()), | ||
follow_system_theme: false, | ||
default_theme: eframe::Theme::Dark, | ||
|
||
#[cfg(target_os = "macos")] | ||
fullsize_content: re_ui::FULLSIZE_CONTENT, | ||
|
||
..Default::default() | ||
}; | ||
|
||
eframe::run_native( | ||
"re_ui example app", | ||
native_options, | ||
Box::new(move |cc| { | ||
let re_ui = re_ui::ReUi::load_and_apply(&cc.egui_ctx); | ||
Box::new(ExampleApp { re_ui }) | ||
}), | ||
); | ||
} | ||
|
||
pub struct ExampleApp { | ||
re_ui: re_ui::ReUi, | ||
} | ||
|
||
impl eframe::App for ExampleApp { | ||
fn update(&mut self, egui_ctx: &egui::Context, frame: &mut eframe::Frame) { | ||
self.top_bar(egui_ctx, frame); | ||
|
||
egui::CentralPanel::default().show(egui_ctx, |ui| { | ||
egui::warn_if_debug_build(ui); | ||
ui.label("Hello world!"); | ||
}); | ||
} | ||
} | ||
|
||
impl ExampleApp { | ||
fn top_bar(&self, egui_ctx: &egui::Context, frame: &mut eframe::Frame) { | ||
let panel_frame = { | ||
egui::Frame { | ||
inner_margin: egui::style::Margin::symmetric(8.0, 2.0), | ||
fill: self.re_ui.design_tokens.top_bar_color, | ||
..Default::default() | ||
} | ||
}; | ||
|
||
let native_pixels_per_point = frame.info().native_pixels_per_point; | ||
let fullscreen = { | ||
#[cfg(target_os = "macos")] | ||
{ | ||
frame.info().window_info.fullscreen | ||
} | ||
#[cfg(not(target_os = "macos"))] | ||
{ | ||
false | ||
} | ||
}; | ||
let top_bar_style = self | ||
.re_ui | ||
.top_bar_style(native_pixels_per_point, fullscreen); | ||
|
||
egui::TopBottomPanel::top("top_bar") | ||
.frame(panel_frame) | ||
.exact_height(top_bar_style.height) | ||
.show(egui_ctx, |ui| { | ||
egui::menu::bar(ui, |ui| { | ||
ui.set_height(top_bar_style.height); | ||
ui.add_space(top_bar_style.indent); | ||
|
||
ui.centered_and_justified(|ui| { | ||
ui.strong("re_ui example app"); | ||
}) | ||
}); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
mod design_tokens; | ||
pub mod icons; | ||
mod static_image_cache; | ||
|
||
pub use design_tokens::DesignTokens; | ||
pub use icons::Icon; | ||
pub use static_image_cache::StaticImageCache; | ||
|
||
// --------------------------------------------------------------------------- | ||
|
||
/// If true, we fill the entire window, except for the close/maximize/minimize buttons in the top-left. | ||
/// See <https://github.com/emilk/egui/pull/2049> | ||
pub const FULLSIZE_CONTENT: bool = cfg!(target_os = "macos"); | ||
|
||
// ---------------------------------------------------------------------------- | ||
|
||
pub struct TopBarStyle { | ||
/// Height of the top bar | ||
pub height: f32, | ||
|
||
/// Extra horizontal space in the top left corner to make room for | ||
/// close/minimize/maximize buttons (on Mac) | ||
pub indent: f32, | ||
} | ||
|
||
// ---------------------------------------------------------------------------- | ||
|
||
use parking_lot::Mutex; | ||
use std::sync::Arc; | ||
|
||
#[derive(Clone)] | ||
pub struct ReUi { | ||
pub egui_ctx: egui::Context, | ||
|
||
/// Colors, styles etc loaded from a design_tokens.json | ||
pub design_tokens: DesignTokens, | ||
|
||
pub static_image_cache: Arc<Mutex<StaticImageCache>>, | ||
} | ||
|
||
impl ReUi { | ||
/// Create [`ReUi`] and apply style to the given egui context. | ||
pub fn load_and_apply(egui_ctx: &egui::Context) -> Self { | ||
Self { | ||
egui_ctx: egui_ctx.clone(), | ||
design_tokens: DesignTokens::load_and_apply(egui_ctx), | ||
static_image_cache: Arc::new(Mutex::new(StaticImageCache::default())), | ||
} | ||
} | ||
|
||
pub fn rerun_logo(&self) -> Arc<egui_extras::RetainedImage> { | ||
if self.egui_ctx.style().visuals.dark_mode { | ||
self.static_image_cache.lock().get( | ||
"logo_dark_mode", | ||
include_bytes!("../data/logo_dark_mode.png"), | ||
) | ||
} else { | ||
self.static_image_cache.lock().get( | ||
"logo_light_mode", | ||
include_bytes!("../data/logo_light_mode.png"), | ||
) | ||
} | ||
} | ||
|
||
#[allow(clippy::unused_self)] | ||
pub fn panel_frame(&self) -> egui::Frame { | ||
let style = self.egui_ctx.style(); | ||
egui::Frame { | ||
fill: style.visuals.window_fill(), | ||
inner_margin: egui::style::Margin::same(4.0), | ||
..Default::default() | ||
} | ||
} | ||
|
||
#[allow(clippy::unused_self)] | ||
pub fn hovering_frame(&self) -> egui::Frame { | ||
let style = self.egui_ctx.style(); | ||
egui::Frame { | ||
inner_margin: egui::style::Margin::same(2.0), | ||
outer_margin: egui::style::Margin::same(4.0), | ||
rounding: 4.0.into(), | ||
fill: style.visuals.window_fill(), | ||
stroke: style.visuals.window_stroke(), | ||
..Default::default() | ||
} | ||
} | ||
|
||
#[allow(clippy::unused_self)] | ||
pub fn warning_text(&self, text: impl Into<String>) -> egui::RichText { | ||
let style = self.egui_ctx.style(); | ||
egui::RichText::new(text) | ||
.italics() | ||
.color(style.visuals.warn_fg_color) | ||
} | ||
|
||
pub fn loop_selection_color() -> egui::Color32 { | ||
egui::Color32::from_rgb(40, 200, 130) | ||
} | ||
|
||
/// Paint a watermark | ||
pub fn paint_watermark(&self) { | ||
use egui::*; | ||
let logo = self.rerun_logo(); | ||
let screen_rect = self.egui_ctx.input().screen_rect; | ||
let size = logo.size_vec2(); | ||
let rect = Align2::RIGHT_BOTTOM | ||
.align_size_within_rect(size, screen_rect) | ||
.translate(-Vec2::splat(16.0)); | ||
let mut mesh = Mesh::with_texture(logo.texture_id(&self.egui_ctx)); | ||
let uv = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)); | ||
mesh.add_rect_with_uv(rect, uv, Color32::WHITE); | ||
self.egui_ctx.debug_painter().add(Shape::mesh(mesh)); | ||
} | ||
|
||
pub fn top_bar_style( | ||
&self, | ||
native_pixels_per_point: Option<f32>, | ||
fullscreen: bool, | ||
) -> TopBarStyle { | ||
let gui_zoom = if let Some(native_pixels_per_point) = native_pixels_per_point { | ||
native_pixels_per_point / self.egui_ctx.pixels_per_point() | ||
} else { | ||
1.0 | ||
}; | ||
|
||
// On Mac, we share the same space as the native red/yellow/green close/minimize/maximize buttons. | ||
// This means we need to make room for them. | ||
let make_room_for_window_buttons = { | ||
#[cfg(target_os = "macos")] | ||
{ | ||
crate::FULLSIZE_CONTENT && !fullscreen | ||
} | ||
#[cfg(not(target_os = "macos"))] | ||
{ | ||
_ = fullscreen; | ||
false | ||
} | ||
}; | ||
|
||
let native_buttons_size_in_native_scale = egui::vec2(64.0, 24.0); // source: I measured /emilk | ||
|
||
let height = if make_room_for_window_buttons { | ||
// Use more vertical space when zoomed in… | ||
let height = native_buttons_size_in_native_scale.y; | ||
|
||
// …but never shrink below the native button height when zoomed out. | ||
height.max(gui_zoom * native_buttons_size_in_native_scale.y) | ||
} else { | ||
self.egui_ctx.style().spacing.interact_size.y | ||
}; | ||
|
||
let indent = if make_room_for_window_buttons { | ||
// Always use the same width measured in native GUI coordinates: | ||
gui_zoom * native_buttons_size_in_native_scale.x | ||
} else { | ||
0.0 | ||
}; | ||
|
||
TopBarStyle { height, indent } | ||
} | ||
|
||
pub fn medium_icon_toggle_button( | ||
&self, | ||
ui: &mut egui::Ui, | ||
icon: &Icon, | ||
selected: &mut bool, | ||
) -> egui::Response { | ||
let size_points = egui::Vec2::splat(16.0); // TODO(emilk): get from design tokens | ||
|
||
let image = self.static_image_cache.lock().get(icon.id, icon.png_bytes); | ||
let texture_id = image.texture_id(ui.ctx()); | ||
let tint = if *selected { | ||
ui.visuals().widgets.inactive.fg_stroke.color | ||
} else { | ||
egui::Color32::from_gray(100) // TODO(emilk): get from design tokens | ||
}; | ||
let mut response = ui.add(egui::ImageButton::new(texture_id, size_points).tint(tint)); | ||
if response.clicked() { | ||
*selected = !*selected; | ||
response.mark_changed(); | ||
} | ||
response | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so we're looked to dark theme in this sample? Why do we distinguish in a few places then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are using dark theme everywhere for now. We had light theme support at the start, and I didn't see any reason to remove that support, since we will perhaps want to add it again in the future.