2021-01-02 00:01:01 +00:00
|
|
|
/// All the different demo apps.
|
2021-01-04 00:44:02 +00:00
|
|
|
#[derive(Default)]
|
|
|
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
|
|
|
#[cfg_attr(feature = "persistence", serde(default))]
|
2021-01-01 20:27:10 +00:00
|
|
|
pub struct Apps {
|
2021-01-01 16:11:05 +00:00
|
|
|
demo: crate::apps::DemoApp,
|
2021-04-23 22:34:58 +00:00
|
|
|
easy_mark_editor: crate::easy_mark::EasyMarkEditor,
|
2021-01-04 00:44:02 +00:00
|
|
|
#[cfg(feature = "http")]
|
2021-01-01 16:11:05 +00:00
|
|
|
http: crate::apps::HttpApp,
|
2021-01-01 20:27:10 +00:00
|
|
|
clock: crate::apps::FractalClock,
|
2021-01-01 23:13:34 +00:00
|
|
|
color_test: crate::apps::ColorTest,
|
2021-01-01 20:27:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Apps {
|
|
|
|
fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut dyn epi::App)> {
|
|
|
|
vec![
|
|
|
|
("demo", &mut self.demo as &mut dyn epi::App),
|
2021-01-28 22:50:23 +00:00
|
|
|
("easymark", &mut self.easy_mark_editor as &mut dyn epi::App),
|
2021-01-04 00:44:02 +00:00
|
|
|
#[cfg(feature = "http")]
|
2021-01-01 20:27:10 +00:00
|
|
|
("http", &mut self.http as &mut dyn epi::App),
|
|
|
|
("clock", &mut self.clock as &mut dyn epi::App),
|
2021-01-01 23:13:34 +00:00
|
|
|
("colors", &mut self.color_test as &mut dyn epi::App),
|
2021-01-01 20:27:10 +00:00
|
|
|
]
|
|
|
|
.into_iter()
|
|
|
|
}
|
2021-01-01 16:11:05 +00:00
|
|
|
}
|
|
|
|
|
2021-01-02 00:01:01 +00:00
|
|
|
/// Wraps many demo/test apps into one.
|
2021-01-04 00:44:02 +00:00
|
|
|
#[derive(Default)]
|
|
|
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
|
|
|
#[cfg_attr(feature = "persistence", serde(default))]
|
2021-01-02 00:01:01 +00:00
|
|
|
pub struct WrapApp {
|
|
|
|
selected_anchor: String,
|
|
|
|
apps: Apps,
|
2021-05-27 22:40:22 +00:00
|
|
|
backend_panel: super::backend_panel::BackendPanel,
|
2021-08-20 20:20:45 +00:00
|
|
|
#[cfg_attr(feature = "persistence", serde(skip))]
|
|
|
|
dropped_files: Vec<egui::DroppedFile>,
|
2021-01-02 00:01:01 +00:00
|
|
|
}
|
|
|
|
|
2021-01-01 16:11:05 +00:00
|
|
|
impl epi::App for WrapApp {
|
|
|
|
fn name(&self) -> &str {
|
2021-01-17 13:48:59 +00:00
|
|
|
"egui demo apps"
|
2021-01-01 16:11:05 +00:00
|
|
|
}
|
|
|
|
|
2021-08-18 20:51:16 +00:00
|
|
|
#[allow(unused_variables)]
|
2021-06-07 18:53:33 +00:00
|
|
|
fn setup(
|
|
|
|
&mut self,
|
2021-06-07 18:56:18 +00:00
|
|
|
_ctx: &egui::CtxRef,
|
|
|
|
_frame: &mut epi::Frame<'_>,
|
2021-06-07 18:53:33 +00:00
|
|
|
storage: Option<&dyn epi::Storage>,
|
|
|
|
) {
|
|
|
|
#[cfg(feature = "persistence")]
|
|
|
|
if let Some(storage) = storage {
|
|
|
|
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
|
|
|
}
|
2021-01-01 16:11:05 +00:00
|
|
|
}
|
|
|
|
|
2021-01-04 00:44:02 +00:00
|
|
|
#[cfg(feature = "persistence")]
|
2021-01-01 16:11:05 +00:00
|
|
|
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
|
|
|
epi::set_value(storage, epi::APP_KEY, self);
|
|
|
|
}
|
|
|
|
|
2021-02-12 16:57:53 +00:00
|
|
|
fn max_size_points(&self) -> egui::Vec2 {
|
|
|
|
self.backend_panel.max_size_points_active
|
|
|
|
}
|
|
|
|
|
2021-03-31 18:53:13 +00:00
|
|
|
fn clear_color(&self) -> egui::Rgba {
|
|
|
|
egui::Rgba::TRANSPARENT // we set a `CentralPanel` fill color in `demo_windows.rs`
|
|
|
|
}
|
|
|
|
|
2021-01-02 13:42:43 +00:00
|
|
|
fn warm_up_enabled(&self) -> bool {
|
2021-02-07 13:46:53 +00:00
|
|
|
// The example windows use a lot of emojis. Pre-cache them by running one frame where everything is open
|
2021-04-01 21:07:58 +00:00
|
|
|
cfg!(not(debug_assertions))
|
2021-01-02 13:42:43 +00:00
|
|
|
}
|
|
|
|
|
2021-01-01 19:22:18 +00:00
|
|
|
fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) {
|
2021-01-01 20:27:10 +00:00
|
|
|
if let Some(web_info) = frame.info().web_info.as_ref() {
|
2021-06-22 21:25:54 +00:00
|
|
|
if let Some(anchor) = web_info.web_location_hash.strip_prefix('#') {
|
2021-01-01 20:27:10 +00:00
|
|
|
self.selected_anchor = anchor.to_owned();
|
|
|
|
}
|
|
|
|
}
|
2021-01-01 16:11:05 +00:00
|
|
|
|
2021-01-01 20:27:10 +00:00
|
|
|
if self.selected_anchor.is_empty() {
|
|
|
|
self.selected_anchor = self.apps.iter_mut().next().unwrap().0.to_owned();
|
2021-01-01 16:11:05 +00:00
|
|
|
}
|
|
|
|
|
2021-05-26 20:06:10 +00:00
|
|
|
egui::TopBottomPanel::top("wrap_app_top_bar").show(ctx, |ui| {
|
2021-05-27 17:30:08 +00:00
|
|
|
egui::trace!(ui);
|
2021-05-26 20:06:10 +00:00
|
|
|
self.bar_contents(ui, frame);
|
2021-01-01 16:11:05 +00:00
|
|
|
});
|
|
|
|
|
2021-01-02 00:01:01 +00:00
|
|
|
self.backend_panel.update(ctx, frame);
|
2021-03-07 18:32:27 +00:00
|
|
|
|
2021-01-02 13:42:43 +00:00
|
|
|
if self.backend_panel.open || ctx.memory().everything_is_visible() {
|
2021-05-26 20:06:10 +00:00
|
|
|
egui::SidePanel::left("backend_panel").show(ctx, |ui| {
|
2021-01-02 00:01:01 +00:00
|
|
|
self.backend_panel.ui(ui, frame);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-01-01 20:27:10 +00:00
|
|
|
for (anchor, app) in self.apps.iter_mut() {
|
2021-01-02 13:42:43 +00:00
|
|
|
if anchor == self.selected_anchor || ctx.memory().everything_is_visible() {
|
2021-01-01 20:27:10 +00:00
|
|
|
app.update(ctx, frame);
|
|
|
|
}
|
2021-01-01 16:11:05 +00:00
|
|
|
}
|
2021-03-07 18:32:27 +00:00
|
|
|
|
|
|
|
self.backend_panel.end_of_frame(ctx);
|
2021-08-20 20:20:45 +00:00
|
|
|
|
|
|
|
self.ui_file_drag_and_drop(ctx);
|
2021-01-01 16:11:05 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-01 20:27:10 +00:00
|
|
|
|
2021-05-26 20:06:10 +00:00
|
|
|
impl WrapApp {
|
|
|
|
fn bar_contents(&mut self, ui: &mut egui::Ui, frame: &mut epi::Frame<'_>) {
|
|
|
|
// A menu-bar is a horizontal layout with some special styles applied.
|
|
|
|
// egui::menu::bar(ui, |ui| {
|
|
|
|
ui.horizontal_wrapped(|ui| {
|
|
|
|
dark_light_mode_switch(ui);
|
|
|
|
|
|
|
|
ui.checkbox(&mut self.backend_panel.open, "💻 Backend");
|
|
|
|
ui.separator();
|
|
|
|
|
|
|
|
for (anchor, app) in self.apps.iter_mut() {
|
|
|
|
if ui
|
|
|
|
.selectable_label(self.selected_anchor == anchor, app.name())
|
|
|
|
.clicked()
|
|
|
|
{
|
|
|
|
self.selected_anchor = anchor.to_owned();
|
|
|
|
if frame.is_web() {
|
|
|
|
ui.output().open_url(format!("#{}", anchor));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ui.with_layout(egui::Layout::right_to_left(), |ui| {
|
|
|
|
if false {
|
|
|
|
// TODO: fix the overlap on small screens
|
|
|
|
if let Some(seconds_since_midnight) = frame.info().seconds_since_midnight {
|
|
|
|
if clock_button(ui, seconds_since_midnight).clicked() {
|
|
|
|
self.selected_anchor = "clock".to_owned();
|
|
|
|
if frame.is_web() {
|
|
|
|
ui.output().open_url("#clock");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
egui::warn_if_debug_build(ui);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2021-08-20 20:20:45 +00:00
|
|
|
|
|
|
|
fn ui_file_drag_and_drop(&mut self, ctx: &egui::CtxRef) {
|
|
|
|
use egui::*;
|
|
|
|
|
|
|
|
// Preview hovering files:
|
|
|
|
if !ctx.input().raw.hovered_files.is_empty() {
|
|
|
|
let mut text = "Dropping files:\n".to_owned();
|
|
|
|
for file in &ctx.input().raw.hovered_files {
|
|
|
|
if let Some(path) = &file.path {
|
|
|
|
text += &format!("\n{}", path.display());
|
|
|
|
} else if !file.mime.is_empty() {
|
|
|
|
text += &format!("\n{}", file.mime);
|
|
|
|
} else {
|
|
|
|
text += "\n???";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let painter =
|
|
|
|
ctx.layer_painter(LayerId::new(Order::Foreground, Id::new("file_drop_target")));
|
|
|
|
|
|
|
|
let screen_rect = ctx.input().screen_rect();
|
|
|
|
painter.rect_filled(screen_rect, 0.0, Color32::from_black_alpha(192));
|
|
|
|
painter.text(
|
|
|
|
screen_rect.center(),
|
|
|
|
Align2::CENTER_CENTER,
|
|
|
|
text,
|
|
|
|
TextStyle::Heading,
|
|
|
|
Color32::WHITE,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect dropped files:
|
|
|
|
if !ctx.input().raw.dropped_files.is_empty() {
|
|
|
|
self.dropped_files = ctx.input().raw.dropped_files.clone();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Show dropped files (if any):
|
|
|
|
if !self.dropped_files.is_empty() {
|
|
|
|
let mut open = true;
|
|
|
|
egui::Window::new("Dropped files")
|
|
|
|
.open(&mut open)
|
|
|
|
.show(ctx, |ui| {
|
|
|
|
for file in &self.dropped_files {
|
|
|
|
let mut info = if let Some(path) = &file.path {
|
|
|
|
path.display().to_string()
|
|
|
|
} else if !file.name.is_empty() {
|
|
|
|
file.name.clone()
|
|
|
|
} else {
|
|
|
|
"???".to_owned()
|
|
|
|
};
|
|
|
|
if let Some(bytes) = &file.bytes {
|
|
|
|
info += &format!(" ({} bytes)", bytes.len());
|
|
|
|
}
|
|
|
|
ui.label(info);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if !open {
|
|
|
|
self.dropped_files.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-26 20:06:10 +00:00
|
|
|
}
|
|
|
|
|
2021-01-01 20:27:10 +00:00
|
|
|
fn clock_button(ui: &mut egui::Ui, seconds_since_midnight: f64) -> egui::Response {
|
|
|
|
let time = seconds_since_midnight;
|
|
|
|
let time = format!(
|
|
|
|
"{:02}:{:02}:{:02}.{:02}",
|
|
|
|
(time % (24.0 * 60.0 * 60.0) / 3600.0).floor(),
|
|
|
|
(time % (60.0 * 60.0) / 60.0).floor(),
|
|
|
|
(time % 60.0).floor(),
|
|
|
|
(time % 1.0 * 100.0).floor()
|
|
|
|
);
|
|
|
|
|
|
|
|
ui.add(egui::Button::new(time).text_style(egui::TextStyle::Monospace))
|
|
|
|
}
|
2021-01-02 00:01:01 +00:00
|
|
|
|
2021-02-03 00:08:23 +00:00
|
|
|
/// Show a button to switch to/from dark/light mode (globally).
|
|
|
|
fn dark_light_mode_switch(ui: &mut egui::Ui) {
|
|
|
|
let style: egui::Style = (*ui.ctx().style()).clone();
|
|
|
|
let new_visuals = style.visuals.light_dark_small_toggle_button(ui);
|
|
|
|
if let Some(visuals) = new_visuals {
|
2021-02-03 18:38:50 +00:00
|
|
|
ui.ctx().set_visuals(visuals);
|
2021-02-03 00:08:23 +00:00
|
|
|
}
|
|
|
|
}
|