mirror of
https://github.com/emilk/egui.git
synced 2026-06-27 23:13:13 -04:00
A lot of changes:
Now the Viewport render function is passed to the backend I was needed to update the demo because now Window::show() needs a Fn before was a FnOnce I changed from FnOnce to Fn because we want the window to be rendered separately from the main window That means now the variabiles that we are moving to window need to be Rc or Arc! This is making harder to use `Window`! I'am waiting for more ideas! In eframe now any Window has a property render that stores the current window render function, if has not function App::update will be called insted! New problems in any other window excluding the main window we have no mouse input, i don't know why! Now every window has embedded to false by default, for testing purposes!
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::time::Instant;
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
use winit::event_loop::EventLoopWindowTarget;
|
||||
|
||||
@@ -9,7 +9,7 @@ use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _};
|
||||
|
||||
#[cfg(feature = "accesskit")]
|
||||
use egui::accesskit;
|
||||
use egui::{epaint::ahash::HashMap, window::ViewportBuilder, NumExt as _};
|
||||
use egui::{epaint::ahash::HashMap, window::ViewportBuilder, Context, NumExt as _};
|
||||
#[cfg(feature = "accesskit")]
|
||||
use egui_winit::accesskit_winit;
|
||||
use egui_winit::{native_pixels_per_point, EventResponse, WindowSettings};
|
||||
@@ -432,7 +432,7 @@ impl EpiIntegration {
|
||||
let saved_memory: egui::Memory = self.egui_ctx.memory(|mem| mem.clone());
|
||||
self.egui_ctx
|
||||
.memory_mut(|mem| mem.set_everything_is_visible(true));
|
||||
let full_output = self.update(app, window, egui_winit);
|
||||
let full_output = self.update(app, window, egui_winit, None);
|
||||
self.pending_full_output.append(full_output); // Handle it next frame
|
||||
self.egui_ctx.memory_mut(|mem| *mem = saved_memory); // We don't want to remember that windows were huge.
|
||||
self.egui_ctx.clear_animations();
|
||||
@@ -496,6 +496,7 @@ impl EpiIntegration {
|
||||
app: &mut dyn epi::App,
|
||||
window: &winit::window::Window,
|
||||
egui_winit: &mut egui_winit::State,
|
||||
render: Option<Arc<Box<dyn Fn(&Context) + Sync + Send>>>,
|
||||
) -> egui::FullOutput {
|
||||
let frame_start = std::time::Instant::now();
|
||||
|
||||
@@ -509,7 +510,11 @@ impl EpiIntegration {
|
||||
// Run user code:
|
||||
let full_output = self.egui_ctx.run(raw_input, |egui_ctx| {
|
||||
crate::profile_scope!("App::update");
|
||||
app.update(egui_ctx, &mut self.frame);
|
||||
if let Some(render) = render {
|
||||
(render.as_ref())(egui_ctx)
|
||||
} else {
|
||||
app.update(egui_ctx, &mut self.frame);
|
||||
}
|
||||
});
|
||||
|
||||
self.pending_full_output.append(full_output);
|
||||
|
||||
@@ -411,7 +411,7 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
mod glow_integration {
|
||||
use std::sync::Arc;
|
||||
|
||||
use egui::{epaint::ahash::HashMap, window::ViewportBuilder, NumExt as _};
|
||||
use egui::{epaint::ahash::HashMap, window::ViewportBuilder, Context, NumExt as _};
|
||||
use egui_winit::EventResponse;
|
||||
use glow::HasContext;
|
||||
use glutin::{
|
||||
@@ -454,6 +454,7 @@ mod glow_integration {
|
||||
gl_surface: Option<glutin::surface::Surface<glutin::surface::WindowSurface>>,
|
||||
window: Option<winit::window::Window>,
|
||||
window_id: u64,
|
||||
render: Option<Arc<Box<dyn Fn(&Context) + Sync + Send>>>,
|
||||
pub egui_winit: Option<egui_winit::State>,
|
||||
}
|
||||
/// This struct will contain both persistent and temporary glutin state.
|
||||
@@ -601,6 +602,7 @@ mod glow_integration {
|
||||
window,
|
||||
window_id: 0,
|
||||
egui_winit: None,
|
||||
render: None,
|
||||
}],
|
||||
window_maps,
|
||||
})
|
||||
@@ -1049,6 +1051,7 @@ mod glow_integration {
|
||||
app.as_mut(),
|
||||
win.window.as_ref().unwrap(),
|
||||
win.egui_winit.as_mut().unwrap(),
|
||||
win.render.clone(),
|
||||
);
|
||||
|
||||
integration.handle_platform_output(
|
||||
@@ -1153,7 +1156,7 @@ mod glow_integration {
|
||||
// 0 is the main viewport/window that will not be known by the egui_ctx
|
||||
let mut active_viewports_ids = vec![0];
|
||||
|
||||
viewports.retain_mut(|(id, builder)| {
|
||||
viewports.retain_mut(|(id, builder, render)| {
|
||||
for w in gl_window.windows.iter_mut() {
|
||||
if w.window_id == *id {
|
||||
if w.builder != *builder {
|
||||
@@ -1164,7 +1167,7 @@ mod glow_integration {
|
||||
}
|
||||
w.window = None;
|
||||
w.gl_surface = None;
|
||||
|
||||
w.render = Some(render.clone());
|
||||
w.builder = builder.clone();
|
||||
}
|
||||
active_viewports_ids.push(*id);
|
||||
@@ -1174,13 +1177,14 @@ mod glow_integration {
|
||||
true
|
||||
});
|
||||
|
||||
for (id, builder) in viewports {
|
||||
for (id, builder, render) in viewports {
|
||||
gl_window.windows.push(Window {
|
||||
builder,
|
||||
gl_surface: None,
|
||||
window: None,
|
||||
window_id: id,
|
||||
egui_winit: None,
|
||||
render: Some(render.clone()),
|
||||
});
|
||||
active_viewports_ids.push(id);
|
||||
}
|
||||
@@ -1234,7 +1238,7 @@ mod glow_integration {
|
||||
|
||||
winit::event::Event::MainEventsCleared => {
|
||||
if let Some(running) = self.running.as_mut() {
|
||||
running.gl_window.on_resume(event_loop);
|
||||
let _ = running.gl_window.on_resume(event_loop);
|
||||
}
|
||||
EventResult::Wait
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ impl<'open> Window<'open> {
|
||||
collapsible: true,
|
||||
default_open: true,
|
||||
with_title_bar: true,
|
||||
embedded: true,
|
||||
embedded: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,19 +417,11 @@ impl<'open> Window<'open> {
|
||||
/// Returns `None` if the window is not open (if [`Window::open`] was called with `&mut false`).
|
||||
/// Returns `Some(InnerResponse { inner: None })` if the window is collapsed.
|
||||
#[inline]
|
||||
pub fn show<R>(
|
||||
self,
|
||||
ctx: &Context,
|
||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||
) -> Option<InnerResponse<Option<R>>> {
|
||||
pub fn show(self, ctx: &Context, add_contents: impl Fn(&mut Ui) + Send + Sync + 'static) {
|
||||
self.show_dyn(ctx, Box::new(add_contents))
|
||||
}
|
||||
|
||||
fn show_dyn<'c, R>(
|
||||
self,
|
||||
ctx: &Context,
|
||||
add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
|
||||
) -> Option<InnerResponse<Option<R>>> {
|
||||
fn show_dyn(self, ctx: &Context, add_contents: Box<dyn Fn(&mut Ui) + Send + Sync + 'static>) {
|
||||
let Window {
|
||||
title,
|
||||
open,
|
||||
@@ -444,31 +436,34 @@ impl<'open> Window<'open> {
|
||||
mut window_builder,
|
||||
} = self;
|
||||
|
||||
let is_explicitly_closed = matches!(open, Some(false));
|
||||
let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible());
|
||||
if !embedded {
|
||||
if !is_open {
|
||||
return;
|
||||
}
|
||||
if let Some(size) = ctx.data(|data| data.get_temp::<Vec2>(area.id.with("size"))) {
|
||||
let size = size.round() + ctx.style().spacing.window_margin.sum();
|
||||
window_builder =
|
||||
window_builder.with_inner_size((size.x as u32 + 1, size.y as u32 + 1));
|
||||
}
|
||||
|
||||
ctx.create_viewport(window_builder, move |_window_id| {
|
||||
ctx.create_viewport(window_builder, move |ctx| {
|
||||
let mut frame = frame.unwrap_or(Frame::window(&ctx.style())).rounding(0.0);
|
||||
CentralPanel::default()
|
||||
.frame(frame)
|
||||
.show(ctx, |ui| Some(add_contents(ui)))
|
||||
.show(ctx, |ui| Some(add_contents(ui)));
|
||||
})
|
||||
} else {
|
||||
if ctx.current_viewport() != ctx.current_rendering_viewport() {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
let frame = frame.unwrap_or_else(|| Frame::window(&ctx.style()));
|
||||
|
||||
let is_explicitly_closed = matches!(open, Some(false));
|
||||
let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible());
|
||||
area.show_open_close_animation(ctx, &frame, is_open);
|
||||
|
||||
if !is_open {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
|
||||
let area_id = area.id;
|
||||
@@ -623,7 +618,7 @@ impl<'open> Window<'open> {
|
||||
inner: content_inner,
|
||||
response: full_response,
|
||||
};
|
||||
Some(inner_response)
|
||||
// Some(inner_response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +160,16 @@ struct ContextImpl {
|
||||
|
||||
repaint: Repaint,
|
||||
|
||||
viewports: HashMap<String, (ViewportBuilder, u64, u64, bool)>,
|
||||
viewports: HashMap<
|
||||
String,
|
||||
(
|
||||
ViewportBuilder,
|
||||
u64,
|
||||
u64,
|
||||
bool,
|
||||
Arc<Box<dyn Fn(&Context) + Sync + Send>>,
|
||||
),
|
||||
>,
|
||||
viewport_counter: u64,
|
||||
current_rendering_viewport: u64,
|
||||
viewport_stack: Vec<u64>,
|
||||
@@ -1274,19 +1283,30 @@ impl Context {
|
||||
let repaint_after = self.write(|ctx| ctx.repaint.end_frame());
|
||||
let shapes = self.drain_paint_lists();
|
||||
|
||||
// This is used for,
|
||||
// If there are no viewport that contains the current viewpor that viewport needs to be destroyed!
|
||||
let avalibile_viewports = self.read(|ctx| {
|
||||
let mut avalibile_viewports = vec![0];
|
||||
for (_, (_, id, _, _, _)) in ctx.viewports.iter() {
|
||||
avalibile_viewports.push(*id);
|
||||
}
|
||||
avalibile_viewports
|
||||
});
|
||||
|
||||
let mut viewports = Vec::new();
|
||||
self.write(|ctx| {
|
||||
ctx.viewports.retain(|_, (builder, id, parent, used)| {
|
||||
let out = *used;
|
||||
if ctx.current_rendering_viewport == *parent
|
||||
|| ctx.current_rendering_viewport == *id
|
||||
{
|
||||
*used = false;
|
||||
} else {
|
||||
}
|
||||
viewports.push((*id, builder.clone()));
|
||||
out
|
||||
})
|
||||
ctx.viewports
|
||||
.retain(|_, (builder, id, parent, used, render)| {
|
||||
let out = *used;
|
||||
|
||||
if ctx.current_rendering_viewport == *parent {
|
||||
*used = false;
|
||||
}
|
||||
|
||||
viewports.push((*id, builder.clone(), render.clone()));
|
||||
(out || ctx.current_rendering_viewport != *parent)
|
||||
&& avalibile_viewports.contains(parent)
|
||||
})
|
||||
});
|
||||
|
||||
FullOutput {
|
||||
@@ -1921,16 +1941,17 @@ impl Context {
|
||||
self.write(|ctx| ctx.is_desktop = value)
|
||||
}
|
||||
|
||||
pub fn create_viewport<T>(
|
||||
pub fn create_viewport(
|
||||
&self,
|
||||
window_builder: ViewportBuilder,
|
||||
func: impl FnOnce(u64) -> T,
|
||||
) -> Option<T> {
|
||||
func: impl Fn(&Context) + Send + Sync + 'static,
|
||||
) {
|
||||
let id = self.write(|ctx| {
|
||||
if ctx.is_desktop {
|
||||
if let Some(window) = ctx.viewports.get_mut(&window_builder.title) {
|
||||
window.2 = *ctx.viewport_stack.last().unwrap_or(&0);
|
||||
window.3 = true;
|
||||
window.4 = Arc::new(Box::new(func));
|
||||
window.1
|
||||
} else {
|
||||
let id = ctx.viewport_counter + 1;
|
||||
@@ -1942,6 +1963,7 @@ impl Context {
|
||||
id,
|
||||
*ctx.viewport_stack.last().unwrap_or(&0),
|
||||
true,
|
||||
Arc::new(Box::new(func)),
|
||||
),
|
||||
);
|
||||
id
|
||||
@@ -1954,11 +1976,9 @@ impl Context {
|
||||
ctx.viewport_stack.push(id);
|
||||
ctx.viewport_stack.last().cloned().unwrap_or(0) == ctx.current_rendering_viewport
|
||||
});
|
||||
let out = if should_render { Some(func(id)) } else { None };
|
||||
self.write(|ctx| {
|
||||
ctx.viewport_stack.pop();
|
||||
});
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
//! All the data egui returns to the backend at the end of each frame.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::Context;
|
||||
use crate::{window::ViewportBuilder, WidgetType};
|
||||
|
||||
/// What egui emits each frame from [`crate::Context::run`].
|
||||
///
|
||||
/// The backend should use this.
|
||||
#[derive(Clone, Default, PartialEq)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FullOutput {
|
||||
/// Non-rendering related output.
|
||||
pub platform_output: PlatformOutput,
|
||||
@@ -31,7 +34,11 @@ pub struct FullOutput {
|
||||
/// You can use [`crate::Context::tessellate`] to turn this into triangles.
|
||||
pub shapes: Vec<epaint::ClippedShape>,
|
||||
|
||||
pub viewports: Vec<(u64, ViewportBuilder)>,
|
||||
pub viewports: Vec<(
|
||||
u64,
|
||||
ViewportBuilder,
|
||||
Arc<Box<dyn Fn(&Context) + Sync + Send>>,
|
||||
)>,
|
||||
}
|
||||
|
||||
impl FullOutput {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// How often we repaint the demo app by default
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum RunMode {
|
||||
@@ -353,7 +355,7 @@ struct EguiWindows {
|
||||
output_events: bool,
|
||||
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
output_event_history: std::collections::VecDeque<egui::output::OutputEvent>,
|
||||
output_event_history: Arc<RwLock<std::collections::VecDeque<egui::output::OutputEvent>>>,
|
||||
}
|
||||
|
||||
impl Default for EguiWindows {
|
||||
@@ -397,41 +399,48 @@ impl EguiWindows {
|
||||
output_event_history,
|
||||
} = self;
|
||||
|
||||
ctx.output(|o| {
|
||||
for event in &o.events {
|
||||
output_event_history.push_back(event.clone());
|
||||
{
|
||||
let mut output_event_history = output_event_history.write().unwrap();
|
||||
ctx.output(|o| {
|
||||
for event in &o.events {
|
||||
output_event_history.push_back(event.clone());
|
||||
}
|
||||
});
|
||||
while output_event_history.len() > 1000 {
|
||||
output_event_history.pop_front();
|
||||
}
|
||||
});
|
||||
while output_event_history.len() > 1000 {
|
||||
output_event_history.pop_front();
|
||||
}
|
||||
|
||||
let tmp_ctx = ctx.clone();
|
||||
egui::Window::new("🔧 Settings")
|
||||
.open(settings)
|
||||
.vscroll(true)
|
||||
.show(ctx, |ui| {
|
||||
ctx.settings_ui(ui);
|
||||
.show(ctx, move |ui| {
|
||||
tmp_ctx.settings_ui(ui);
|
||||
});
|
||||
|
||||
let tmp_ctx = ctx.clone();
|
||||
egui::Window::new("🔍 Inspection")
|
||||
.open(inspection)
|
||||
.vscroll(true)
|
||||
.show(ctx, |ui| {
|
||||
ctx.inspection_ui(ui);
|
||||
.show(ctx, move |ui| {
|
||||
tmp_ctx.inspection_ui(ui);
|
||||
});
|
||||
|
||||
let tmp_ctx = ctx.clone();
|
||||
egui::Window::new("📝 Memory")
|
||||
.open(memory)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
ctx.memory_ui(ui);
|
||||
.show(ctx, move |ui| {
|
||||
tmp_ctx.memory_ui(ui);
|
||||
});
|
||||
|
||||
let tmp_output_event_history = output_event_history.clone();
|
||||
egui::Window::new("📤 Output Events")
|
||||
.open(output_events)
|
||||
.resizable(true)
|
||||
.default_width(520.0)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
ui.label(
|
||||
"Recent output events from egui. \
|
||||
These are emitted when you interact with widgets, or move focus between them with TAB. \
|
||||
@@ -443,7 +452,7 @@ impl EguiWindows {
|
||||
egui::ScrollArea::vertical()
|
||||
.stick_to_bottom(true)
|
||||
.show(ui, |ui| {
|
||||
for event in output_event_history {
|
||||
for event in &*tmp_output_event_history.read().unwrap() {
|
||||
ui.label(format!("{:?}", event));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ use eframe::glow;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use core::any::Any;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
@@ -147,7 +148,7 @@ pub struct WrapApp {
|
||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
||||
custom3d: Option<crate::apps::Custom3d>,
|
||||
|
||||
dropped_files: Vec<egui::DroppedFile>,
|
||||
dropped_files: Arc<RwLock<Vec<egui::DroppedFile>>>,
|
||||
}
|
||||
|
||||
impl WrapApp {
|
||||
@@ -412,17 +413,20 @@ impl WrapApp {
|
||||
// Collect dropped files:
|
||||
ctx.input(|i| {
|
||||
if !i.raw.dropped_files.is_empty() {
|
||||
self.dropped_files = i.raw.dropped_files.clone();
|
||||
*self.dropped_files.write().unwrap() = i.raw.dropped_files.clone();
|
||||
}
|
||||
});
|
||||
|
||||
// Show dropped files (if any):
|
||||
if !self.dropped_files.is_empty() {
|
||||
let is_empty = self.dropped_files.read().unwrap().is_empty();
|
||||
if !is_empty {
|
||||
let mut open = true;
|
||||
let dropped_files = self.dropped_files.clone();
|
||||
egui::Window::new("Dropped files")
|
||||
.open(&mut open)
|
||||
.show(ctx, |ui| {
|
||||
for file in &self.dropped_files {
|
||||
.show(ctx, move |ui| {
|
||||
let dropped_files = &*dropped_files.read().unwrap();
|
||||
for file in dropped_files {
|
||||
let mut info = if let Some(path) = &file.path {
|
||||
path.display().to_string()
|
||||
} else if !file.name.is_empty() {
|
||||
@@ -437,7 +441,7 @@ impl WrapApp {
|
||||
}
|
||||
});
|
||||
if !open {
|
||||
self.dropped_files.clear();
|
||||
self.dropped_files.write().unwrap().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ impl super::Demo for About {
|
||||
.open(open)
|
||||
.show(ctx, |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
Self::default().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct CodeEditor {
|
||||
pub struct CodeEditorData {
|
||||
language: String,
|
||||
code: String,
|
||||
}
|
||||
|
||||
impl Default for CodeEditor {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct CodeEditor {
|
||||
data: Arc<RwLock<CodeEditorData>>,
|
||||
}
|
||||
|
||||
impl Default for CodeEditorData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
language: "rs".into(),
|
||||
@@ -27,17 +35,18 @@ impl super::Demo for CodeEditor {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
use super::View as _;
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.default_height(500.0)
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
.show(ctx, move |ui| clone.clone().ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for CodeEditor {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
let Self { language, code } = self;
|
||||
let CodeEditorData { language, code } = &mut *self.data.write().unwrap();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.set_height(0.0);
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CodeExample {
|
||||
pub struct CodeExampleData {
|
||||
name: String,
|
||||
age: u32,
|
||||
}
|
||||
|
||||
impl Default for CodeExample {
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct CodeExample {
|
||||
data: Arc<RwLock<CodeExampleData>>,
|
||||
}
|
||||
|
||||
impl Default for CodeExampleData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "Arthur".to_owned(),
|
||||
@@ -29,28 +36,29 @@ impl CodeExample {
|
||||
});"#,
|
||||
);
|
||||
// Putting things on the same line using ui.horizontal:
|
||||
let mut data = self.data.write().unwrap();
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Your name: ");
|
||||
ui.text_edit_singleline(&mut self.name);
|
||||
ui.text_edit_singleline(&mut data.name);
|
||||
});
|
||||
ui.end_row();
|
||||
|
||||
show_code(
|
||||
ui,
|
||||
r#"egui::Slider::new(&mut self.age, 0..=120).text("age")"#,
|
||||
r#"egui::Slider::new(&mut data.age, 0..=120).text("age")"#,
|
||||
);
|
||||
ui.add(egui::Slider::new(&mut self.age, 0..=120).text("age"));
|
||||
ui.add(egui::Slider::new(&mut data.age, 0..=120).text("age"));
|
||||
ui.end_row();
|
||||
|
||||
show_code(
|
||||
ui,
|
||||
r#"
|
||||
if ui.button("Click each year").clicked() {
|
||||
self.age += 1;
|
||||
data.age += 1;
|
||||
}"#,
|
||||
);
|
||||
if ui.button("Click each year").clicked() {
|
||||
self.age += 1;
|
||||
data.age += 1;
|
||||
}
|
||||
ui.end_row();
|
||||
|
||||
@@ -58,7 +66,7 @@ impl CodeExample {
|
||||
ui,
|
||||
r#"ui.label(format!("Hello '{}', age {}", self.name, self.age));"#,
|
||||
);
|
||||
ui.label(format!("Hello '{}', age {}", self.name, self.age));
|
||||
ui.label(format!("Hello '{}', age {}", data.name, data.age));
|
||||
ui.end_row();
|
||||
}
|
||||
}
|
||||
@@ -69,13 +77,14 @@ impl super::Demo for CodeExample {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
use super::View;
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.default_size([800.0, 400.0])
|
||||
.vscroll(false)
|
||||
.hscroll(true)
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
.show(ctx, move |ui| clone.clone().ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
enum Plot {
|
||||
@@ -14,10 +16,9 @@ fn gaussian(x: f64) -> f64 {
|
||||
fn sigmoid(x: f64) -> f64 {
|
||||
-1.0 + 2.0 / (1.0 + f64::exp(-x))
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct ContextMenus {
|
||||
pub struct ContextMenusData {
|
||||
plot: Plot,
|
||||
show_axes: [bool; 2],
|
||||
allow_drag: bool,
|
||||
@@ -29,7 +30,19 @@ pub struct ContextMenus {
|
||||
height: f32,
|
||||
}
|
||||
|
||||
impl Default for ContextMenus {
|
||||
#[derive(Clone, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct ContextMenus {
|
||||
data: Arc<RwLock<ContextMenusData>>,
|
||||
}
|
||||
|
||||
impl PartialEq for ContextMenus {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.data.read().unwrap() == *other.data.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ContextMenusData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
plot: Plot::Sin,
|
||||
@@ -51,12 +64,13 @@ impl super::Demo for ContextMenus {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
use super::View;
|
||||
egui::Window::new(self.name())
|
||||
.vscroll(false)
|
||||
.resizable(false)
|
||||
.open(open)
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
.show(ctx, move |ui| clone.clone().ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,13 +87,14 @@ impl super::View for ContextMenus {
|
||||
ui.label("Right-click plot to edit it!");
|
||||
ui.horizontal(|ui| {
|
||||
self.example_plot(ui).context_menu(|ui| {
|
||||
let data = &mut *self.data.write().unwrap();
|
||||
ui.menu_button("Plot", |ui| {
|
||||
if ui.radio_value(&mut self.plot, Plot::Sin, "Sin").clicked()
|
||||
if ui.radio_value(&mut data.plot, Plot::Sin, "Sin").clicked()
|
||||
|| ui
|
||||
.radio_value(&mut self.plot, Plot::Bell, "Gaussian")
|
||||
.radio_value(&mut data.plot, Plot::Bell, "Gaussian")
|
||||
.clicked()
|
||||
|| ui
|
||||
.radio_value(&mut self.plot, Plot::Sigmoid, "Sigmoid")
|
||||
.radio_value(&mut data.plot, Plot::Sigmoid, "Sigmoid")
|
||||
.clicked()
|
||||
{
|
||||
ui.close_menu();
|
||||
@@ -87,22 +102,22 @@ impl super::View for ContextMenus {
|
||||
});
|
||||
egui::Grid::new("button_grid").show(ui, |ui| {
|
||||
ui.add(
|
||||
egui::DragValue::new(&mut self.width)
|
||||
egui::DragValue::new(&mut data.width)
|
||||
.speed(1.0)
|
||||
.prefix("Width:"),
|
||||
);
|
||||
ui.add(
|
||||
egui::DragValue::new(&mut self.height)
|
||||
egui::DragValue::new(&mut data.height)
|
||||
.speed(1.0)
|
||||
.prefix("Height:"),
|
||||
);
|
||||
ui.end_row();
|
||||
ui.checkbox(&mut self.show_axes[0], "x-Axis");
|
||||
ui.checkbox(&mut self.show_axes[1], "y-Axis");
|
||||
ui.checkbox(&mut data.show_axes[0], "x-Axis");
|
||||
ui.checkbox(&mut data.show_axes[1], "y-Axis");
|
||||
ui.end_row();
|
||||
if ui.checkbox(&mut self.allow_drag, "Drag").changed()
|
||||
|| ui.checkbox(&mut self.allow_zoom, "Zoom").changed()
|
||||
|| ui.checkbox(&mut self.allow_scroll, "Scroll").changed()
|
||||
if ui.checkbox(&mut data.allow_drag, "Drag").changed()
|
||||
|| ui.checkbox(&mut data.allow_zoom, "Zoom").changed()
|
||||
|| ui.checkbox(&mut data.allow_scroll, "Scroll").changed()
|
||||
{
|
||||
ui.close_menu();
|
||||
}
|
||||
@@ -117,6 +132,7 @@ impl super::View for ContextMenus {
|
||||
|
||||
impl ContextMenus {
|
||||
fn example_plot(&self, ui: &mut egui::Ui) -> egui::Response {
|
||||
let data = &mut *self.data.write().unwrap();
|
||||
use egui::plot::{Line, PlotPoints};
|
||||
let n = 128;
|
||||
let line = Line::new(
|
||||
@@ -124,7 +140,7 @@ impl ContextMenus {
|
||||
.map(|i| {
|
||||
use std::f64::consts::TAU;
|
||||
let x = egui::remap(i as f64, 0.0..=n as f64, -TAU..=TAU);
|
||||
match self.plot {
|
||||
match data.plot {
|
||||
Plot::Sin => [x, x.sin()],
|
||||
Plot::Bell => [x, 10.0 * gaussian(x)],
|
||||
Plot::Sigmoid => [x, sigmoid(x)],
|
||||
@@ -133,14 +149,14 @@ impl ContextMenus {
|
||||
.collect::<PlotPoints>(),
|
||||
);
|
||||
egui::plot::Plot::new("example_plot")
|
||||
.show_axes(self.show_axes)
|
||||
.allow_drag(self.allow_drag)
|
||||
.allow_zoom(self.allow_zoom)
|
||||
.allow_scroll(self.allow_scroll)
|
||||
.center_x_axis(self.center_x_axis)
|
||||
.center_x_axis(self.center_y_axis)
|
||||
.width(self.width)
|
||||
.height(self.height)
|
||||
.show_axes(data.show_axes)
|
||||
.allow_drag(data.allow_drag)
|
||||
.allow_zoom(data.allow_zoom)
|
||||
.allow_scroll(data.allow_scroll)
|
||||
.center_x_axis(data.center_x_axis)
|
||||
.center_x_axis(data.center_y_axis)
|
||||
.width(data.width)
|
||||
.height(data.height)
|
||||
.data_aspect(1.0)
|
||||
.show(ui, |plot_ui| plot_ui.line(line))
|
||||
.response
|
||||
|
||||
@@ -16,7 +16,7 @@ impl super::Demo for DancingStrings {
|
||||
.open(open)
|
||||
.default_size(vec2(512.0, 256.0))
|
||||
.vscroll(false)
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
.show(ctx, |ui| Self::default().ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use egui::{Context, Modifiers, ScrollArea, Ui};
|
||||
use std::collections::BTreeSet;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use super::About;
|
||||
use super::Demo;
|
||||
@@ -12,7 +14,7 @@ use crate::is_mobile;
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
struct Demos {
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
demos: Vec<Box<dyn Demo>>,
|
||||
demos: Vec<Box<dyn Demo + Sync + Send>>,
|
||||
|
||||
open: BTreeSet<String>,
|
||||
}
|
||||
@@ -45,7 +47,7 @@ impl Default for Demos {
|
||||
}
|
||||
|
||||
impl Demos {
|
||||
pub fn from_demos(demos: Vec<Box<dyn Demo>>) -> Self {
|
||||
pub fn from_demos(demos: Vec<Box<dyn Demo + Sync + Send>>) -> Self {
|
||||
let mut open = BTreeSet::new();
|
||||
open.insert(
|
||||
super::widget_gallery::WidgetGallery::default()
|
||||
@@ -81,7 +83,7 @@ impl Demos {
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
struct Tests {
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
demos: Vec<Box<dyn Demo>>,
|
||||
demos: Vec<Box<dyn Demo + Sync + Send>>,
|
||||
|
||||
open: BTreeSet<String>,
|
||||
}
|
||||
@@ -101,7 +103,7 @@ impl Default for Tests {
|
||||
}
|
||||
|
||||
impl Tests {
|
||||
pub fn from_demos(demos: Vec<Box<dyn Demo>>) -> Self {
|
||||
pub fn from_demos(demos: Vec<Box<dyn Demo + Sync + Send>>) -> Self {
|
||||
let mut open = BTreeSet::new();
|
||||
open.insert(
|
||||
super::widget_gallery::WidgetGallery::default()
|
||||
@@ -146,16 +148,23 @@ fn set_open(open: &mut BTreeSet<String>, key: &'static str, is_open: bool) {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// A menu bar in which you can select different demo windows to show.
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct DemoWindows {
|
||||
pub struct DemoWindowsData {
|
||||
about_is_open: bool,
|
||||
about: About,
|
||||
demos: Demos,
|
||||
tests: Tests,
|
||||
}
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DemoWindows {
|
||||
data: Arc<RwLock<DemoWindowsData>>,
|
||||
}
|
||||
|
||||
impl Default for DemoWindows {
|
||||
impl Default for DemoWindowsData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
about_is_open: true,
|
||||
@@ -177,32 +186,36 @@ impl DemoWindows {
|
||||
}
|
||||
|
||||
fn mobile_ui(&mut self, ctx: &Context) {
|
||||
if self.about_is_open {
|
||||
let mut about_is_open = self.data.read().unwrap().about_is_open;
|
||||
if about_is_open {
|
||||
let screen_size = ctx.input(|i| i.screen_rect.size());
|
||||
let default_width = (screen_size.x - 20.0).min(400.0);
|
||||
|
||||
let mut close = false;
|
||||
egui::Window::new(self.about.name())
|
||||
let close = Arc::new(RwLock::new(false));
|
||||
let close_ = close.clone();
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.data.read().unwrap().about.name())
|
||||
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
|
||||
.default_width(default_width)
|
||||
.default_height(ctx.available_rect().height() - 46.0)
|
||||
.vscroll(true)
|
||||
.open(&mut self.about_is_open)
|
||||
.open(&mut about_is_open)
|
||||
.resizable(false)
|
||||
.collapsible(false)
|
||||
.show(ctx, |ui| {
|
||||
self.about.ui(ui);
|
||||
.show(ctx, move |ui| {
|
||||
let close = close.clone();
|
||||
clone.data.write().unwrap().about.ui(ui);
|
||||
ui.add_space(12.0);
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
if ui
|
||||
.button(egui::RichText::new("Continue to the demo!").size(20.0))
|
||||
.clicked()
|
||||
{
|
||||
close = true;
|
||||
*close.write().unwrap() = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
self.about_is_open &= !close;
|
||||
self.data.write().unwrap().about_is_open &= !*close_.read().unwrap();
|
||||
} else {
|
||||
self.mobile_top_bar(ctx);
|
||||
self.show_windows(ctx);
|
||||
@@ -275,20 +288,22 @@ impl DemoWindows {
|
||||
|
||||
/// Show the open windows.
|
||||
fn show_windows(&mut self, ctx: &Context) {
|
||||
self.about.show(ctx, &mut self.about_is_open);
|
||||
self.demos.windows(ctx);
|
||||
self.tests.windows(ctx);
|
||||
let data = &mut *self.data.write().unwrap();
|
||||
data.about.show(ctx, &mut data.about_is_open);
|
||||
data.demos.windows(ctx);
|
||||
data.tests.windows(ctx);
|
||||
}
|
||||
|
||||
fn demo_list_ui(&mut self, ui: &mut egui::Ui) {
|
||||
ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| {
|
||||
ui.toggle_value(&mut self.about_is_open, self.about.name());
|
||||
let data = &mut *self.data.write().unwrap();
|
||||
ui.toggle_value(&mut data.about_is_open, data.about.name());
|
||||
|
||||
ui.separator();
|
||||
self.demos.checkboxes(ui);
|
||||
data.demos.checkboxes(ui);
|
||||
ui.separator();
|
||||
self.tests.checkboxes(ui);
|
||||
data.tests.checkboxes(ui);
|
||||
ui.separator();
|
||||
|
||||
if ui.button("Organize windows").clicked() {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use egui::*;
|
||||
|
||||
pub fn drag_source(ui: &mut Ui, id: Id, body: impl FnOnce(&mut Ui)) {
|
||||
@@ -75,24 +77,32 @@ pub fn drop_target<R>(
|
||||
InnerResponse::new(ret, response)
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct DragAndDropDemo {
|
||||
/// columns with items
|
||||
columns: Vec<Vec<String>>,
|
||||
columns: Arc<RwLock<Vec<Vec<String>>>>,
|
||||
}
|
||||
|
||||
impl PartialEq for DragAndDropDemo {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.columns.read().unwrap() == *other.columns.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DragAndDropDemo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
columns: vec![
|
||||
vec!["Item A", "Item B", "Item C"],
|
||||
vec!["Item D", "Item E"],
|
||||
vec!["Item F", "Item G", "Item H"],
|
||||
]
|
||||
.into_iter()
|
||||
.map(|v| v.into_iter().map(ToString::to_string).collect())
|
||||
.collect(),
|
||||
columns: Arc::new(RwLock::new(
|
||||
vec![
|
||||
vec!["Item A", "Item B", "Item C"],
|
||||
vec!["Item D", "Item E"],
|
||||
vec!["Item F", "Item G", "Item H"],
|
||||
]
|
||||
.into_iter()
|
||||
.map(|v| v.into_iter().map(ToString::to_string).collect())
|
||||
.collect(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,13 +113,14 @@ impl super::Demo for DragAndDropDemo {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
use super::View as _;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
.default_size(vec2(256.0, 256.0))
|
||||
.vscroll(false)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
.show(ctx, move |ui| clone.clone().ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +132,9 @@ impl super::View for DragAndDropDemo {
|
||||
let id_source = "my_drag_and_drop_demo";
|
||||
let mut source_col_row = None;
|
||||
let mut drop_col = None;
|
||||
ui.columns(self.columns.len(), |uis| {
|
||||
for (col_idx, column) in self.columns.clone().into_iter().enumerate() {
|
||||
let mut columns = self.columns.write().unwrap();
|
||||
ui.columns(columns.len(), |uis| {
|
||||
for (col_idx, column) in columns.clone().into_iter().enumerate() {
|
||||
let ui = &mut uis[col_idx];
|
||||
let can_accept_what_is_being_dragged = true; // We accept anything being dragged (for now) ¯\_(ツ)_/¯
|
||||
let response = drop_target(ui, can_accept_what_is_being_dragged, |ui| {
|
||||
@@ -133,7 +145,7 @@ impl super::View for DragAndDropDemo {
|
||||
let response = ui.add(Label::new(item).sense(Sense::click()));
|
||||
response.context_menu(|ui| {
|
||||
if ui.button("Remove").clicked() {
|
||||
self.columns[col_idx].remove(row_idx);
|
||||
columns[col_idx].remove(row_idx);
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
@@ -148,7 +160,7 @@ impl super::View for DragAndDropDemo {
|
||||
|
||||
let response = response.context_menu(|ui| {
|
||||
if ui.button("New Item").clicked() {
|
||||
self.columns[col_idx].push("New Item".to_owned());
|
||||
columns[col_idx].push("New Item".to_owned());
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
@@ -164,8 +176,8 @@ impl super::View for DragAndDropDemo {
|
||||
if let Some(drop_col) = drop_col {
|
||||
if ui.input(|i| i.pointer.any_released()) {
|
||||
// do the drop:
|
||||
let item = self.columns[source_col].remove(source_row);
|
||||
self.columns[drop_col].push(item);
|
||||
let item = columns[source_col].remove(source_row);
|
||||
columns[drop_col].push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct FontBook {
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
pub struct FontBookData {
|
||||
filter: String,
|
||||
font_id: egui::FontId,
|
||||
named_chars: BTreeMap<egui::FontFamily, BTreeMap<char, String>>,
|
||||
}
|
||||
|
||||
impl Default for FontBook {
|
||||
#[derive(Default, Clone)]
|
||||
pub struct FontBook {
|
||||
data: Arc<RwLock<FontBookData>>,
|
||||
}
|
||||
|
||||
impl Default for FontBookData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
filter: Default::default(),
|
||||
@@ -22,10 +29,13 @@ impl super::Demo for FontBook {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name()).open(open).show(ctx, |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
});
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +45,12 @@ impl super::View for FontBook {
|
||||
ui.add(crate::egui_github_link_file!());
|
||||
});
|
||||
|
||||
let data = &mut *self.data.write().unwrap();
|
||||
|
||||
ui.label(format!(
|
||||
"The selected font supports {} characters.",
|
||||
self.named_chars
|
||||
.get(&self.font_id.family)
|
||||
data.named_chars
|
||||
.get(&data.font_id.family)
|
||||
.map(|map| map.len())
|
||||
.unwrap_or_default()
|
||||
));
|
||||
@@ -55,22 +67,22 @@ impl super::View for FontBook {
|
||||
|
||||
ui.separator();
|
||||
|
||||
egui::introspection::font_id_ui(ui, &mut self.font_id);
|
||||
egui::introspection::font_id_ui(ui, &mut data.font_id);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Filter:");
|
||||
ui.add(egui::TextEdit::singleline(&mut self.filter).desired_width(120.0));
|
||||
self.filter = self.filter.to_lowercase();
|
||||
ui.add(egui::TextEdit::singleline(&mut data.filter).desired_width(120.0));
|
||||
data.filter = data.filter.to_lowercase();
|
||||
if ui.button("x").clicked() {
|
||||
self.filter.clear();
|
||||
data.filter.clear();
|
||||
}
|
||||
});
|
||||
|
||||
let filter = &self.filter;
|
||||
let named_chars = self
|
||||
let filter = &data.filter;
|
||||
let named_chars = data
|
||||
.named_chars
|
||||
.entry(self.font_id.family.clone())
|
||||
.or_insert_with(|| available_characters(ui, self.font_id.family.clone()));
|
||||
.entry(data.font_id.family.clone())
|
||||
.or_insert_with(|| available_characters(ui, data.font_id.family.clone()));
|
||||
|
||||
ui.separator();
|
||||
|
||||
@@ -81,13 +93,13 @@ impl super::View for FontBook {
|
||||
for (&chr, name) in named_chars {
|
||||
if filter.is_empty() || name.contains(filter) || *filter == chr.to_string() {
|
||||
let button = egui::Button::new(
|
||||
egui::RichText::new(chr.to_string()).font(self.font_id.clone()),
|
||||
egui::RichText::new(chr.to_string()).font(data.font_id.clone()),
|
||||
)
|
||||
.frame(false);
|
||||
|
||||
let tooltip_ui = |ui: &mut egui::Ui| {
|
||||
ui.label(
|
||||
egui::RichText::new(chr.to_string()).font(self.font_id.clone()),
|
||||
egui::RichText::new(chr.to_string()).font(data.font_id.clone()),
|
||||
);
|
||||
ui.label(format!("{}\nU+{:X}\n\nClick to copy", name, chr as u32));
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ impl super::Demo for Highlighting {
|
||||
.open(open)
|
||||
.show(ctx, |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
Self::default().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use egui::*;
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct LayoutTest {
|
||||
pub struct LayoutTestData {
|
||||
// Identical to contents of `egui::Layout`
|
||||
layout: LayoutSettings,
|
||||
|
||||
@@ -11,7 +13,14 @@ pub struct LayoutTest {
|
||||
wrap_row_height: f32,
|
||||
}
|
||||
|
||||
impl Default for LayoutTest {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct LayoutTest {
|
||||
data: Arc<RwLock<LayoutTestData>>,
|
||||
}
|
||||
|
||||
impl Default for LayoutTestData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
layout: LayoutSettings::top_down(),
|
||||
@@ -79,12 +88,13 @@ impl super::Demo for LayoutTest {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -93,23 +103,24 @@ impl super::View for LayoutTest {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
ui.label("Tests and demonstrates the limits of the egui layouts");
|
||||
self.content_ui(ui);
|
||||
let data = self.data.write().unwrap();
|
||||
Resize::default()
|
||||
.default_size([150.0, 200.0])
|
||||
.show(ui, |ui| {
|
||||
if self.layout.main_wrap {
|
||||
if self.layout.main_dir.is_horizontal() {
|
||||
if data.layout.main_wrap {
|
||||
if data.layout.main_dir.is_horizontal() {
|
||||
ui.allocate_ui(
|
||||
vec2(ui.available_size_before_wrap().x, self.wrap_row_height),
|
||||
|ui| ui.with_layout(self.layout.layout(), demo_ui),
|
||||
vec2(ui.available_size_before_wrap().x, data.wrap_row_height),
|
||||
|ui| ui.with_layout(data.layout.layout(), demo_ui),
|
||||
);
|
||||
} else {
|
||||
ui.allocate_ui(
|
||||
vec2(self.wrap_column_width, ui.available_size_before_wrap().y),
|
||||
|ui| ui.with_layout(self.layout.layout(), demo_ui),
|
||||
vec2(data.wrap_column_width, ui.available_size_before_wrap().y),
|
||||
|ui| ui.with_layout(data.layout.layout(), demo_ui),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
ui.with_layout(self.layout.layout(), demo_ui);
|
||||
ui.with_layout(data.layout.layout(), demo_ui);
|
||||
}
|
||||
});
|
||||
ui.label("Resize to see effect");
|
||||
@@ -118,15 +129,16 @@ impl super::View for LayoutTest {
|
||||
|
||||
impl LayoutTest {
|
||||
pub fn content_ui(&mut self, ui: &mut Ui) {
|
||||
let mut data = self.data.write().unwrap();
|
||||
ui.horizontal(|ui| {
|
||||
ui.selectable_value(&mut self.layout, LayoutSettings::top_down(), "Top-down");
|
||||
ui.selectable_value(&mut data.layout, LayoutSettings::top_down(), "Top-down");
|
||||
ui.selectable_value(
|
||||
&mut self.layout,
|
||||
&mut data.layout,
|
||||
LayoutSettings::top_down_justified_centered(),
|
||||
"Top-down, centered and justified",
|
||||
);
|
||||
ui.selectable_value(
|
||||
&mut self.layout,
|
||||
&mut data.layout,
|
||||
LayoutSettings::horizontal_wrapped(),
|
||||
"Horizontal wrapped",
|
||||
);
|
||||
@@ -140,20 +152,20 @@ impl LayoutTest {
|
||||
Direction::TopDown,
|
||||
Direction::BottomUp,
|
||||
] {
|
||||
ui.radio_value(&mut self.layout.main_dir, dir, format!("{:?}", dir));
|
||||
ui.radio_value(&mut data.layout.main_dir, dir, format!("{:?}", dir));
|
||||
}
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.checkbox(&mut self.layout.main_wrap, "Main wrap")
|
||||
ui.checkbox(&mut data.layout.main_wrap, "Main wrap")
|
||||
.on_hover_text("Wrap when next widget doesn't fit the current row/column");
|
||||
|
||||
if self.layout.main_wrap {
|
||||
if self.layout.main_dir.is_horizontal() {
|
||||
ui.add(Slider::new(&mut self.wrap_row_height, 0.0..=200.0).text("Row height"));
|
||||
if data.layout.main_wrap {
|
||||
if data.layout.main_dir.is_horizontal() {
|
||||
ui.add(Slider::new(&mut data.wrap_row_height, 0.0..=200.0).text("Row height"));
|
||||
} else {
|
||||
ui.add(
|
||||
Slider::new(&mut self.wrap_column_width, 0.0..=200.0).text("Column width"),
|
||||
Slider::new(&mut data.wrap_column_width, 0.0..=200.0).text("Column width"),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -162,11 +174,11 @@ impl LayoutTest {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Cross Align:");
|
||||
for &align in &[Align::Min, Align::Center, Align::Max] {
|
||||
ui.radio_value(&mut self.layout.cross_align, align, format!("{:?}", align));
|
||||
ui.radio_value(&mut data.layout.cross_align, align, format!("{:?}", align));
|
||||
}
|
||||
});
|
||||
|
||||
ui.checkbox(&mut self.layout.cross_justify, "Cross Justified")
|
||||
ui.checkbox(&mut data.layout.cross_justify, "Cross Justified")
|
||||
.on_hover_text("Try to fill full width/height (e.g. buttons)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use super::*;
|
||||
use crate::LOREM_IPSUM;
|
||||
use egui::{epaint::text::TextWrapping, *};
|
||||
|
||||
/// Showcase some ui code
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct MiscDemoWindow {
|
||||
pub struct MiscDemoWindowData {
|
||||
num_columns: usize,
|
||||
|
||||
break_anywhere: bool,
|
||||
@@ -21,10 +22,17 @@ pub struct MiscDemoWindow {
|
||||
dummy_bool: bool,
|
||||
dummy_usize: usize,
|
||||
}
|
||||
/// Showcase some ui code
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct MiscDemoWindow {
|
||||
data: Arc<RwLock<MiscDemoWindowData>>,
|
||||
}
|
||||
|
||||
impl Default for MiscDemoWindow {
|
||||
fn default() -> MiscDemoWindow {
|
||||
MiscDemoWindow {
|
||||
impl Default for MiscDemoWindowData {
|
||||
fn default() -> MiscDemoWindowData {
|
||||
MiscDemoWindowData {
|
||||
num_columns: 2,
|
||||
|
||||
max_rows: 2,
|
||||
@@ -49,11 +57,12 @@ impl Demo for MiscDemoWindow {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
.vscroll(true)
|
||||
.hscroll(true)
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
.show(ctx, move |ui| clone.clone().ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,10 +70,13 @@ impl View for MiscDemoWindow {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
ui.set_min_width(250.0);
|
||||
|
||||
let mut data = self.data.write().unwrap();
|
||||
let data = &mut *data;
|
||||
|
||||
CollapsingHeader::new("Widgets")
|
||||
.default_open(true)
|
||||
.show(ui, |ui| {
|
||||
self.widgets.ui(ui);
|
||||
data.widgets.ui(ui);
|
||||
});
|
||||
|
||||
CollapsingHeader::new("Text layout")
|
||||
@@ -72,25 +84,25 @@ impl View for MiscDemoWindow {
|
||||
.show(ui, |ui| {
|
||||
text_layout_ui(
|
||||
ui,
|
||||
&mut self.max_rows,
|
||||
&mut self.break_anywhere,
|
||||
&mut self.overflow_character,
|
||||
&mut data.max_rows,
|
||||
&mut data.break_anywhere,
|
||||
&mut data.overflow_character,
|
||||
);
|
||||
});
|
||||
|
||||
CollapsingHeader::new("Colors")
|
||||
.default_open(false)
|
||||
.show(ui, |ui| {
|
||||
self.colors.ui(ui);
|
||||
data.colors.ui(ui);
|
||||
});
|
||||
|
||||
CollapsingHeader::new("Custom Collapsing Header")
|
||||
.default_open(false)
|
||||
.show(ui, |ui| self.custom_collapsing_header.ui(ui));
|
||||
.show(ui, |ui| data.custom_collapsing_header.ui(ui));
|
||||
|
||||
CollapsingHeader::new("Tree")
|
||||
.default_open(false)
|
||||
.show(ui, |ui| self.tree.ui(ui));
|
||||
.show(ui, |ui| data.tree.ui(ui));
|
||||
|
||||
CollapsingHeader::new("Checkboxes")
|
||||
.default_open(false)
|
||||
@@ -99,28 +111,28 @@ impl View for MiscDemoWindow {
|
||||
ui.spacing_mut().item_spacing = Vec2::ZERO;
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
for _ in 0..64 {
|
||||
ui.checkbox(&mut self.dummy_bool, "");
|
||||
ui.checkbox(&mut data.dummy_bool, "");
|
||||
}
|
||||
});
|
||||
ui.checkbox(&mut self.dummy_bool, "checkbox");
|
||||
ui.checkbox(&mut data.dummy_bool, "checkbox");
|
||||
|
||||
ui.label("Radiobuttons are similar:");
|
||||
ui.spacing_mut().item_spacing = Vec2::ZERO;
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
for i in 0..64 {
|
||||
ui.radio_value(&mut self.dummy_usize, i, "");
|
||||
ui.radio_value(&mut data.dummy_usize, i, "");
|
||||
}
|
||||
});
|
||||
ui.radio_value(&mut self.dummy_usize, 64, "radio_value");
|
||||
ui.radio_value(&mut data.dummy_usize, 64, "radio_value");
|
||||
});
|
||||
|
||||
ui.collapsing("Columns", |ui| {
|
||||
ui.add(Slider::new(&mut self.num_columns, 1..=10).text("Columns"));
|
||||
ui.columns(self.num_columns, |cols| {
|
||||
ui.add(Slider::new(&mut data.num_columns, 1..=10).text("Columns"));
|
||||
ui.columns(data.num_columns, |cols| {
|
||||
for (i, col) in cols.iter_mut().enumerate() {
|
||||
col.label(format!("Column {} out of {}", i + 1, self.num_columns));
|
||||
if i + 1 == self.num_columns && col.button("Delete this").clicked() {
|
||||
self.num_columns -= 1;
|
||||
col.label(format!("Column {} out of {}", i + 1, data.num_columns));
|
||||
if i + 1 == data.num_columns && col.button("Delete this").clicked() {
|
||||
data.num_columns -= 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -128,7 +140,7 @@ impl View for MiscDemoWindow {
|
||||
|
||||
CollapsingHeader::new("Test box rendering")
|
||||
.default_open(false)
|
||||
.show(ui, |ui| self.box_painting.ui(ui));
|
||||
.show(ui, |ui| data.box_painting.ui(ui));
|
||||
|
||||
CollapsingHeader::new("Resize")
|
||||
.default_open(false)
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use egui::{
|
||||
emath::{RectTransform, Rot2},
|
||||
vec2, Color32, Frame, Pos2, Rect, Sense, Stroke, Vec2,
|
||||
};
|
||||
|
||||
pub struct MultiTouch {
|
||||
pub struct MultiTouchData {
|
||||
rotation: f32,
|
||||
translation: Vec2,
|
||||
zoom: f32,
|
||||
last_touch_time: f64,
|
||||
}
|
||||
|
||||
impl Default for MultiTouch {
|
||||
#[derive(Clone, Default)]
|
||||
pub struct MultiTouch {
|
||||
data: Arc<RwLock<MultiTouchData>>,
|
||||
}
|
||||
|
||||
impl Default for MultiTouchData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
rotation: 0.,
|
||||
@@ -27,13 +34,14 @@ impl super::Demo for MultiTouch {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.default_size(vec2(512.0, 512.0))
|
||||
.resizable(true)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -83,22 +91,24 @@ impl super::View for MultiTouch {
|
||||
// color and width:
|
||||
let mut stroke_width = 1.;
|
||||
if let Some(multi_touch) = ui.ctx().multi_touch() {
|
||||
let mut data = self.data.write().unwrap();
|
||||
// This adjusts the current zoom factor and rotation angle according to the dynamic
|
||||
// change (for the current frame) of the touch gesture:
|
||||
self.zoom *= multi_touch.zoom_delta;
|
||||
self.rotation += multi_touch.rotation_delta;
|
||||
data.zoom *= multi_touch.zoom_delta;
|
||||
data.rotation += multi_touch.rotation_delta;
|
||||
// the translation we get from `multi_touch` needs to be scaled down to the
|
||||
// normalized coordinates we use as the basis for painting:
|
||||
self.translation += to_screen.inverse().scale() * multi_touch.translation_delta;
|
||||
data.translation += to_screen.inverse().scale() * multi_touch.translation_delta;
|
||||
// touch pressure will make the arrow thicker (not all touch devices support this):
|
||||
stroke_width += 10. * multi_touch.force;
|
||||
|
||||
self.last_touch_time = ui.input(|i| i.time);
|
||||
data.last_touch_time = ui.input(|i| i.time);
|
||||
} else {
|
||||
self.slowly_reset(ui);
|
||||
}
|
||||
let zoom_and_rotate = self.zoom * Rot2::from_angle(self.rotation);
|
||||
let arrow_start_offset = self.translation + zoom_and_rotate * vec2(-0.5, 0.5);
|
||||
let data = self.data.read().unwrap();
|
||||
let zoom_and_rotate = data.zoom * Rot2::from_angle(data.rotation);
|
||||
let arrow_start_offset = data.translation + zoom_and_rotate * vec2(-0.5, 0.5);
|
||||
|
||||
// Paints an arrow pointing from bottom-left (-0.5, 0.5) to top-right (0.5, -0.5), but
|
||||
// scaled, rotated, and translated according to the current touch gesture:
|
||||
@@ -118,7 +128,8 @@ impl MultiTouch {
|
||||
// This has nothing to do with the touch gesture. It just smoothly brings the
|
||||
// painted arrow back into its original position, for a nice visual effect:
|
||||
|
||||
let time_since_last_touch = (ui.input(|i| i.time) - self.last_touch_time) as f32;
|
||||
let mut data = self.data.write().unwrap();
|
||||
let time_since_last_touch = (ui.input(|i| i.time) - data.last_touch_time) as f32;
|
||||
|
||||
let delay = 0.5;
|
||||
if time_since_last_touch < delay {
|
||||
@@ -129,15 +140,15 @@ impl MultiTouch {
|
||||
egui::remap_clamp(time_since_last_touch, delay..=1.0, 1.0..=0.0).powf(4.0);
|
||||
|
||||
if half_life <= 1e-3 {
|
||||
self.zoom = 1.0;
|
||||
self.rotation = 0.0;
|
||||
self.translation = Vec2::ZERO;
|
||||
data.zoom = 1.0;
|
||||
data.rotation = 0.0;
|
||||
data.translation = Vec2::ZERO;
|
||||
} else {
|
||||
let dt = ui.input(|i| i.unstable_dt);
|
||||
let half_life_factor = (-(2_f32.ln()) / half_life * dt).exp();
|
||||
self.zoom = 1. + ((self.zoom - 1.) * half_life_factor);
|
||||
self.rotation *= half_life_factor;
|
||||
self.translation *= half_life_factor;
|
||||
data.zoom = 1. + ((data.zoom - 1.) * half_life_factor);
|
||||
data.rotation *= half_life_factor;
|
||||
data.translation *= half_life_factor;
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use egui::epaint::{CubicBezierShape, PathShape, QuadraticBezierShape};
|
||||
use egui::*;
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct PaintBezier {
|
||||
pub struct PaintBezierData {
|
||||
/// Bézier curve degree, it can be 3, 4.
|
||||
degree: usize,
|
||||
|
||||
@@ -22,7 +24,14 @@ pub struct PaintBezier {
|
||||
bounding_box_stroke: Stroke,
|
||||
}
|
||||
|
||||
impl Default for PaintBezier {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct PaintBezier {
|
||||
data: Arc<RwLock<PaintBezierData>>,
|
||||
}
|
||||
|
||||
impl Default for PaintBezierData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
degree: 4,
|
||||
@@ -42,14 +51,15 @@ impl Default for PaintBezier {
|
||||
|
||||
impl PaintBezier {
|
||||
pub fn ui_control(&mut self, ui: &mut egui::Ui) {
|
||||
let mut data = self.data.write().unwrap();
|
||||
ui.collapsing("Colors", |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Fill color:");
|
||||
ui.color_edit_button_srgba(&mut self.fill);
|
||||
ui.color_edit_button_srgba(&mut data.fill);
|
||||
});
|
||||
egui::stroke_ui(ui, &mut self.stroke, "Curve Stroke");
|
||||
egui::stroke_ui(ui, &mut self.aux_stroke, "Auxiliary Stroke");
|
||||
egui::stroke_ui(ui, &mut self.bounding_box_stroke, "Bounding Box Stroke");
|
||||
egui::stroke_ui(ui, &mut data.stroke, "Curve Stroke");
|
||||
egui::stroke_ui(ui, &mut data.aux_stroke, "Auxiliary Stroke");
|
||||
egui::stroke_ui(ui, &mut data.bounding_box_stroke, "Bounding Box Stroke");
|
||||
});
|
||||
|
||||
ui.collapsing("Global tessellation options", |ui| {
|
||||
@@ -59,8 +69,8 @@ impl PaintBezier {
|
||||
.tessellation_options_mut(|to| *to = tessellation_options);
|
||||
});
|
||||
|
||||
ui.radio_value(&mut self.degree, 3, "Quadratic Bézier");
|
||||
ui.radio_value(&mut self.degree, 4, "Cubic Bézier");
|
||||
ui.radio_value(&mut data.degree, 3, "Quadratic Bézier");
|
||||
ui.radio_value(&mut data.degree, 4, "Cubic Bézier");
|
||||
ui.label("Move the points by dragging them.");
|
||||
ui.small("Only convex curves can be accurately filled.");
|
||||
}
|
||||
@@ -75,12 +85,14 @@ impl PaintBezier {
|
||||
);
|
||||
|
||||
let control_point_radius = 8.0;
|
||||
let mut data = self.data.write().unwrap();
|
||||
let data = &mut *data;
|
||||
|
||||
let control_point_shapes: Vec<Shape> = self
|
||||
let control_point_shapes: Vec<Shape> = data
|
||||
.control_points
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.take(self.degree)
|
||||
.take(data.degree)
|
||||
.map(|(i, point)| {
|
||||
let size = Vec2::splat(2.0 * control_point_radius);
|
||||
|
||||
@@ -99,33 +111,33 @@ impl PaintBezier {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let points_in_screen: Vec<Pos2> = self
|
||||
let points_in_screen: Vec<Pos2> = data
|
||||
.control_points
|
||||
.iter()
|
||||
.take(self.degree)
|
||||
.take(data.degree)
|
||||
.map(|p| to_screen * *p)
|
||||
.collect();
|
||||
|
||||
match self.degree {
|
||||
match data.degree {
|
||||
3 => {
|
||||
let points = points_in_screen.clone().try_into().unwrap();
|
||||
let shape =
|
||||
QuadraticBezierShape::from_points_stroke(points, true, self.fill, self.stroke);
|
||||
QuadraticBezierShape::from_points_stroke(points, true, data.fill, data.stroke);
|
||||
painter.add(epaint::RectShape::stroke(
|
||||
shape.visual_bounding_rect(),
|
||||
0.0,
|
||||
self.bounding_box_stroke,
|
||||
data.bounding_box_stroke,
|
||||
));
|
||||
painter.add(shape);
|
||||
}
|
||||
4 => {
|
||||
let points = points_in_screen.clone().try_into().unwrap();
|
||||
let shape =
|
||||
CubicBezierShape::from_points_stroke(points, true, self.fill, self.stroke);
|
||||
CubicBezierShape::from_points_stroke(points, true, data.fill, data.stroke);
|
||||
painter.add(epaint::RectShape::stroke(
|
||||
shape.visual_bounding_rect(),
|
||||
0.0,
|
||||
self.bounding_box_stroke,
|
||||
data.bounding_box_stroke,
|
||||
));
|
||||
painter.add(shape);
|
||||
}
|
||||
@@ -134,7 +146,7 @@ impl PaintBezier {
|
||||
}
|
||||
};
|
||||
|
||||
painter.add(PathShape::line(points_in_screen, self.aux_stroke));
|
||||
painter.add(PathShape::line(points_in_screen, data.aux_stroke));
|
||||
painter.extend(control_point_shapes);
|
||||
|
||||
response
|
||||
@@ -147,13 +159,14 @@ impl super::Demo for PaintBezier {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
use super::View as _;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
.vscroll(false)
|
||||
.resizable(false)
|
||||
.default_size([300.0, 350.0])
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
.show(ctx, move |ui| clone.clone().ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
use egui::*;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use egui::*;
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct Painting {
|
||||
pub struct PaintingData {
|
||||
/// in 0-1 normalized coordinates
|
||||
lines: Vec<Vec<Pos2>>,
|
||||
stroke: Stroke,
|
||||
}
|
||||
|
||||
impl Default for Painting {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Painting {
|
||||
data: Arc<RwLock<PaintingData>>,
|
||||
}
|
||||
|
||||
impl Default for PaintingData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
lines: Default::default(),
|
||||
@@ -19,11 +27,12 @@ impl Default for Painting {
|
||||
|
||||
impl Painting {
|
||||
pub fn ui_control(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
||||
let mut data = self.data.write().unwrap();
|
||||
ui.horizontal(|ui| {
|
||||
egui::stroke_ui(ui, &mut self.stroke, "Stroke");
|
||||
egui::stroke_ui(ui, &mut data.stroke, "Stroke");
|
||||
ui.separator();
|
||||
if ui.button("Clear Painting").clicked() {
|
||||
self.lines.clear();
|
||||
data.lines.clear();
|
||||
}
|
||||
})
|
||||
.response
|
||||
@@ -39,11 +48,13 @@ impl Painting {
|
||||
);
|
||||
let from_screen = to_screen.inverse();
|
||||
|
||||
if self.lines.is_empty() {
|
||||
self.lines.push(vec![]);
|
||||
let mut data = self.data.write().unwrap();
|
||||
|
||||
if data.lines.is_empty() {
|
||||
data.lines.push(vec![]);
|
||||
}
|
||||
|
||||
let current_line = self.lines.last_mut().unwrap();
|
||||
let current_line = data.lines.last_mut().unwrap();
|
||||
|
||||
if let Some(pointer_pos) = response.interact_pointer_pos() {
|
||||
let canvas_pos = from_screen * pointer_pos;
|
||||
@@ -52,17 +63,17 @@ impl Painting {
|
||||
response.mark_changed();
|
||||
}
|
||||
} else if !current_line.is_empty() {
|
||||
self.lines.push(vec![]);
|
||||
data.lines.push(vec![]);
|
||||
response.mark_changed();
|
||||
}
|
||||
|
||||
let shapes = self
|
||||
let shapes = data
|
||||
.lines
|
||||
.iter()
|
||||
.filter(|line| line.len() >= 2)
|
||||
.map(|line| {
|
||||
let points: Vec<Pos2> = line.iter().map(|p| to_screen * *p).collect();
|
||||
egui::Shape::line(points, self.stroke)
|
||||
egui::Shape::line(points, data.stroke)
|
||||
});
|
||||
|
||||
painter.extend(shapes);
|
||||
@@ -77,12 +88,13 @@ impl super::Demo for Painting {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
use super::View as _;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
.default_size(vec2(512.0, 512.0))
|
||||
.vscroll(false)
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
.show(ctx, move |ui| clone.clone().ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::f64::consts::TAU;
|
||||
use std::ops::RangeInclusive;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use egui::plot::{AxisBools, GridInput, GridMark, PlotResponse};
|
||||
use egui::*;
|
||||
@@ -32,7 +33,7 @@ impl Default for Panel {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[derive(PartialEq, Default)]
|
||||
pub struct PlotDemo {
|
||||
pub struct PlotDemoData {
|
||||
line_demo: LineDemo,
|
||||
marker_demo: MarkerDemo,
|
||||
legend_demo: LegendDemo,
|
||||
@@ -44,18 +45,30 @@ pub struct PlotDemo {
|
||||
open_panel: Panel,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct PlotDemo {
|
||||
data: Arc<RwLock<PlotDemoData>>,
|
||||
}
|
||||
|
||||
impl PartialEq for PlotDemo {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.data.read().unwrap() == *other.data.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Demo for PlotDemo {
|
||||
fn name(&self) -> &'static str {
|
||||
"🗠 Plot"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
use super::View as _;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
.default_size(vec2(400.0, 400.0))
|
||||
.vscroll(false)
|
||||
.show(ctx, |ui| self.ui(ui));
|
||||
.show(ctx, move |ui| clone.clone().ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,43 +90,44 @@ impl super::View for PlotDemo {
|
||||
ui.add(crate::egui_github_link_file!());
|
||||
});
|
||||
});
|
||||
let mut data = self.data.write().unwrap();
|
||||
ui.separator();
|
||||
ui.horizontal(|ui| {
|
||||
ui.selectable_value(&mut self.open_panel, Panel::Lines, "Lines");
|
||||
ui.selectable_value(&mut self.open_panel, Panel::Markers, "Markers");
|
||||
ui.selectable_value(&mut self.open_panel, Panel::Legend, "Legend");
|
||||
ui.selectable_value(&mut self.open_panel, Panel::Charts, "Charts");
|
||||
ui.selectable_value(&mut self.open_panel, Panel::Items, "Items");
|
||||
ui.selectable_value(&mut self.open_panel, Panel::Interaction, "Interaction");
|
||||
ui.selectable_value(&mut self.open_panel, Panel::CustomAxes, "Custom Axes");
|
||||
ui.selectable_value(&mut self.open_panel, Panel::LinkedAxes, "Linked Axes");
|
||||
ui.selectable_value(&mut data.open_panel, Panel::Lines, "Lines");
|
||||
ui.selectable_value(&mut data.open_panel, Panel::Markers, "Markers");
|
||||
ui.selectable_value(&mut data.open_panel, Panel::Legend, "Legend");
|
||||
ui.selectable_value(&mut data.open_panel, Panel::Charts, "Charts");
|
||||
ui.selectable_value(&mut data.open_panel, Panel::Items, "Items");
|
||||
ui.selectable_value(&mut data.open_panel, Panel::Interaction, "Interaction");
|
||||
ui.selectable_value(&mut data.open_panel, Panel::CustomAxes, "Custom Axes");
|
||||
ui.selectable_value(&mut data.open_panel, Panel::LinkedAxes, "Linked Axes");
|
||||
});
|
||||
ui.separator();
|
||||
|
||||
match self.open_panel {
|
||||
match data.open_panel {
|
||||
Panel::Lines => {
|
||||
self.line_demo.ui(ui);
|
||||
data.line_demo.ui(ui);
|
||||
}
|
||||
Panel::Markers => {
|
||||
self.marker_demo.ui(ui);
|
||||
data.marker_demo.ui(ui);
|
||||
}
|
||||
Panel::Legend => {
|
||||
self.legend_demo.ui(ui);
|
||||
data.legend_demo.ui(ui);
|
||||
}
|
||||
Panel::Charts => {
|
||||
self.charts_demo.ui(ui);
|
||||
data.charts_demo.ui(ui);
|
||||
}
|
||||
Panel::Items => {
|
||||
self.items_demo.ui(ui);
|
||||
data.items_demo.ui(ui);
|
||||
}
|
||||
Panel::Interaction => {
|
||||
self.interaction_demo.ui(ui);
|
||||
data.interaction_demo.ui(ui);
|
||||
}
|
||||
Panel::CustomAxes => {
|
||||
self.custom_axes_demo.ui(ui);
|
||||
data.custom_axes_demo.ui(ui);
|
||||
}
|
||||
Panel::LinkedAxes => {
|
||||
self.linked_axes_demo.ui(ui);
|
||||
data.linked_axes_demo.ui(ui);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use egui::*;
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
@@ -19,49 +21,64 @@ impl Default for ScrollDemo {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Default, PartialEq)]
|
||||
pub struct Scrolling {
|
||||
pub struct ScrollingData {
|
||||
demo: ScrollDemo,
|
||||
scroll_to: ScrollTo,
|
||||
scroll_stick_to: ScrollStickTo,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Scrolling {
|
||||
data: Arc<RwLock<ScrollingData>>,
|
||||
}
|
||||
|
||||
impl PartialEq for Scrolling {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.data.read().unwrap() == *other.data.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Demo for Scrolling {
|
||||
fn name(&self) -> &'static str {
|
||||
"↕ Scrolling"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for Scrolling {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
let mut data = self.data.write().unwrap();
|
||||
ui.horizontal(|ui| {
|
||||
ui.selectable_value(&mut self.demo, ScrollDemo::ScrollTo, "Scroll to");
|
||||
ui.selectable_value(&mut data.demo, ScrollDemo::ScrollTo, "Scroll to");
|
||||
ui.selectable_value(
|
||||
&mut self.demo,
|
||||
&mut data.demo,
|
||||
ScrollDemo::ManyLines,
|
||||
"Scroll a lot of lines",
|
||||
);
|
||||
ui.selectable_value(
|
||||
&mut self.demo,
|
||||
&mut data.demo,
|
||||
ScrollDemo::LargeCanvas,
|
||||
"Scroll a large canvas",
|
||||
);
|
||||
ui.selectable_value(&mut self.demo, ScrollDemo::StickToEnd, "Stick to end");
|
||||
ui.selectable_value(&mut self.demo, ScrollDemo::Bidirectional, "Bidirectional");
|
||||
ui.selectable_value(&mut data.demo, ScrollDemo::StickToEnd, "Stick to end");
|
||||
ui.selectable_value(&mut data.demo, ScrollDemo::Bidirectional, "Bidirectional");
|
||||
});
|
||||
ui.separator();
|
||||
match self.demo {
|
||||
match data.demo {
|
||||
ScrollDemo::ScrollTo => {
|
||||
self.scroll_to.ui(ui);
|
||||
data.scroll_to.ui(ui);
|
||||
}
|
||||
ScrollDemo::ManyLines => {
|
||||
huge_content_lines(ui);
|
||||
@@ -70,7 +87,7 @@ impl super::View for Scrolling {
|
||||
huge_content_painter(ui);
|
||||
}
|
||||
ScrollDemo::StickToEnd => {
|
||||
self.scroll_stick_to.ui(ui);
|
||||
data.scroll_stick_to.ui(ui);
|
||||
}
|
||||
ScrollDemo::Bidirectional => {
|
||||
egui::ScrollArea::both().show(ui, |ui| {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use egui::*;
|
||||
use std::f64::INFINITY;
|
||||
use std::{
|
||||
f64::INFINITY,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
/// Showcase sliders
|
||||
#[derive(PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct Sliders {
|
||||
pub struct SlidersData {
|
||||
pub min: f64,
|
||||
pub max: f64,
|
||||
pub logarithmic: bool,
|
||||
@@ -19,7 +22,20 @@ pub struct Sliders {
|
||||
pub trailing_fill: bool,
|
||||
}
|
||||
|
||||
impl Default for Sliders {
|
||||
#[derive(Clone, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct Sliders {
|
||||
pub data: Arc<RwLock<SlidersData>>,
|
||||
}
|
||||
|
||||
impl PartialEq for Sliders {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.data.read().unwrap() == *other.data.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SlidersData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
min: 0.0,
|
||||
@@ -43,149 +59,154 @@ impl super::Demo for Sliders {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for Sliders {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
let Self {
|
||||
min,
|
||||
max,
|
||||
logarithmic,
|
||||
clamp_to_range,
|
||||
smart_aim,
|
||||
step,
|
||||
use_steps,
|
||||
integer,
|
||||
vertical,
|
||||
value,
|
||||
trailing_fill,
|
||||
} = self;
|
||||
{
|
||||
let SlidersData {
|
||||
min,
|
||||
max,
|
||||
logarithmic,
|
||||
clamp_to_range,
|
||||
smart_aim,
|
||||
step,
|
||||
use_steps,
|
||||
integer,
|
||||
vertical,
|
||||
value,
|
||||
trailing_fill,
|
||||
} = &mut *self.data.write().unwrap();
|
||||
|
||||
ui.label("You can click a slider value to edit it with the keyboard.");
|
||||
ui.label("You can click a slider value to edit it with the keyboard.");
|
||||
|
||||
let (type_min, type_max) = if *integer {
|
||||
((i32::MIN as f64), (i32::MAX as f64))
|
||||
} else if *logarithmic {
|
||||
(-INFINITY, INFINITY)
|
||||
} else {
|
||||
(-1e5, 1e5) // linear sliders make little sense with huge numbers
|
||||
};
|
||||
let (type_min, type_max) = if *integer {
|
||||
((i32::MIN as f64), (i32::MAX as f64))
|
||||
} else if *logarithmic {
|
||||
(-INFINITY, INFINITY)
|
||||
} else {
|
||||
(-1e5, 1e5) // linear sliders make little sense with huge numbers
|
||||
};
|
||||
|
||||
*min = min.clamp(type_min, type_max);
|
||||
*max = max.clamp(type_min, type_max);
|
||||
*min = min.clamp(type_min, type_max);
|
||||
*max = max.clamp(type_min, type_max);
|
||||
|
||||
let orientation = if *vertical {
|
||||
SliderOrientation::Vertical
|
||||
} else {
|
||||
SliderOrientation::Horizontal
|
||||
};
|
||||
let orientation = if *vertical {
|
||||
SliderOrientation::Vertical
|
||||
} else {
|
||||
SliderOrientation::Horizontal
|
||||
};
|
||||
|
||||
let istep = if *use_steps { *step } else { 0.0 };
|
||||
if *integer {
|
||||
let mut value_i32 = *value as i32;
|
||||
ui.add(
|
||||
Slider::new(&mut value_i32, (*min as i32)..=(*max as i32))
|
||||
.logarithmic(*logarithmic)
|
||||
.clamp_to_range(*clamp_to_range)
|
||||
.smart_aim(*smart_aim)
|
||||
.orientation(orientation)
|
||||
.text("i32 demo slider")
|
||||
.step_by(istep)
|
||||
.trailing_fill(*trailing_fill),
|
||||
);
|
||||
*value = value_i32 as f64;
|
||||
} else {
|
||||
ui.add(
|
||||
Slider::new(value, (*min)..=(*max))
|
||||
.logarithmic(*logarithmic)
|
||||
.clamp_to_range(*clamp_to_range)
|
||||
.smart_aim(*smart_aim)
|
||||
.orientation(orientation)
|
||||
.text("f64 demo slider")
|
||||
.step_by(istep)
|
||||
.trailing_fill(*trailing_fill),
|
||||
);
|
||||
let istep = if *use_steps { *step } else { 0.0 };
|
||||
if *integer {
|
||||
let mut value_i32 = *value as i32;
|
||||
ui.add(
|
||||
Slider::new(&mut value_i32, (*min as i32)..=(*max as i32))
|
||||
.logarithmic(*logarithmic)
|
||||
.clamp_to_range(*clamp_to_range)
|
||||
.smart_aim(*smart_aim)
|
||||
.orientation(orientation)
|
||||
.text("i32 demo slider")
|
||||
.step_by(istep)
|
||||
.trailing_fill(*trailing_fill),
|
||||
);
|
||||
*value = value_i32 as f64;
|
||||
} else {
|
||||
ui.add(
|
||||
Slider::new(value, (*min)..=(*max))
|
||||
.logarithmic(*logarithmic)
|
||||
.clamp_to_range(*clamp_to_range)
|
||||
.smart_aim(*smart_aim)
|
||||
.orientation(orientation)
|
||||
.text("f64 demo slider")
|
||||
.step_by(istep)
|
||||
.trailing_fill(*trailing_fill),
|
||||
);
|
||||
|
||||
ui.label(
|
||||
"Sliders will intelligently pick how many decimals to show. \
|
||||
ui.label(
|
||||
"Sliders will intelligently pick how many decimals to show. \
|
||||
You can always see the full precision value by hovering the value.",
|
||||
);
|
||||
|
||||
if ui.button("Assign PI").clicked() {
|
||||
self.data.write().unwrap().value = std::f64::consts::PI;
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label("Slider range:");
|
||||
ui.add(
|
||||
Slider::new(min, type_min..=type_max)
|
||||
.logarithmic(true)
|
||||
.smart_aim(*smart_aim)
|
||||
.text("left")
|
||||
.trailing_fill(*trailing_fill),
|
||||
);
|
||||
ui.add(
|
||||
Slider::new(max, type_min..=type_max)
|
||||
.logarithmic(true)
|
||||
.smart_aim(*smart_aim)
|
||||
.text("right")
|
||||
.trailing_fill(*trailing_fill),
|
||||
);
|
||||
|
||||
if ui.button("Assign PI").clicked() {
|
||||
self.value = std::f64::consts::PI;
|
||||
ui.separator();
|
||||
|
||||
ui.checkbox(trailing_fill, "Toggle trailing color");
|
||||
ui.label("When enabled, trailing color will be painted up until the circle.");
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.checkbox(use_steps, "Use steps");
|
||||
ui.label("When enabled, the minimal value change would be restricted to a given step.");
|
||||
if *use_steps {
|
||||
ui.add(egui::DragValue::new(step).speed(1.0));
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Slider type:");
|
||||
ui.radio_value(integer, true, "i32");
|
||||
ui.radio_value(integer, false, "f64");
|
||||
})
|
||||
.response
|
||||
.on_hover_text("All numeric types (f32, usize, …) are supported.");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Slider orientation:");
|
||||
ui.radio_value(vertical, false, "Horizontal");
|
||||
ui.radio_value(vertical, true, "Vertical");
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.checkbox(logarithmic, "Logarithmic");
|
||||
ui.label("Logarithmic sliders are great for when you want to span a huge range, i.e. from zero to a million.");
|
||||
ui.label("Logarithmic sliders can include infinity and zero.");
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.checkbox(clamp_to_range, "Clamp to range");
|
||||
ui.label(
|
||||
"If true, the slider will clamp incoming and outgoing values to the given range.",
|
||||
);
|
||||
ui.label("If false, the slider can shows values outside its range, and you can manually enter values outside the range.");
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.checkbox(smart_aim, "Smart Aim");
|
||||
ui.label("Smart Aim will guide you towards round values when you drag the slider so you you are more likely to hit 250 than 247.23");
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label("Slider range:");
|
||||
ui.add(
|
||||
Slider::new(min, type_min..=type_max)
|
||||
.logarithmic(true)
|
||||
.smart_aim(*smart_aim)
|
||||
.text("left")
|
||||
.trailing_fill(*trailing_fill),
|
||||
);
|
||||
ui.add(
|
||||
Slider::new(max, type_min..=type_max)
|
||||
.logarithmic(true)
|
||||
.smart_aim(*smart_aim)
|
||||
.text("right")
|
||||
.trailing_fill(*trailing_fill),
|
||||
);
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.checkbox(trailing_fill, "Toggle trailing color");
|
||||
ui.label("When enabled, trailing color will be painted up until the circle.");
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.checkbox(use_steps, "Use steps");
|
||||
ui.label("When enabled, the minimal value change would be restricted to a given step.");
|
||||
if *use_steps {
|
||||
ui.add(egui::DragValue::new(step).speed(1.0));
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Slider type:");
|
||||
ui.radio_value(integer, true, "i32");
|
||||
ui.radio_value(integer, false, "f64");
|
||||
})
|
||||
.response
|
||||
.on_hover_text("All numeric types (f32, usize, …) are supported.");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Slider orientation:");
|
||||
ui.radio_value(vertical, false, "Horizontal");
|
||||
ui.radio_value(vertical, true, "Vertical");
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.checkbox(logarithmic, "Logarithmic");
|
||||
ui.label("Logarithmic sliders are great for when you want to span a huge range, i.e. from zero to a million.");
|
||||
ui.label("Logarithmic sliders can include infinity and zero.");
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.checkbox(clamp_to_range, "Clamp to range");
|
||||
ui.label("If true, the slider will clamp incoming and outgoing values to the given range.");
|
||||
ui.label("If false, the slider can shows values outside its range, and you can manually enter values outside the range.");
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.checkbox(smart_aim, "Smart Aim");
|
||||
ui.label("Smart Aim will guide you towards round values when you drag the slider so you you are more likely to hit 250 than 247.23");
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.vertical_centered(|ui| {
|
||||
egui::reset_button(ui, self);
|
||||
ui.add(crate::egui_github_link_file!());
|
||||
|
||||
@@ -18,7 +18,7 @@ impl super::Demo for StripDemo {
|
||||
.default_width(400.0)
|
||||
.show(ctx, |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
Self::default().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
enum DemoType {
|
||||
@@ -6,9 +8,8 @@ enum DemoType {
|
||||
ManyHeterogenous,
|
||||
}
|
||||
|
||||
/// Shows off a table with dynamic layout
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct TableDemo {
|
||||
pub struct TableDemoData {
|
||||
demo: DemoType,
|
||||
striped: bool,
|
||||
resizable: bool,
|
||||
@@ -17,7 +18,14 @@ pub struct TableDemo {
|
||||
scroll_to_row: Option<usize>,
|
||||
}
|
||||
|
||||
impl Default for TableDemo {
|
||||
/// Shows off a table with dynamic layout
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct TableDemo {
|
||||
data: Arc<RwLock<TableDemoData>>,
|
||||
}
|
||||
|
||||
impl Default for TableDemoData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
demo: DemoType::Manual,
|
||||
@@ -36,13 +44,14 @@ impl super::Demo for TableDemo {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(true)
|
||||
.default_width(400.0)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -51,52 +60,55 @@ const NUM_MANUAL_ROWS: usize = 20;
|
||||
|
||||
impl super::View for TableDemo {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.checkbox(&mut self.striped, "Striped");
|
||||
ui.checkbox(&mut self.resizable, "Resizable columns");
|
||||
{
|
||||
let mut data = self.data.write().unwrap();
|
||||
ui.vertical(|ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.checkbox(&mut data.striped, "Striped");
|
||||
ui.checkbox(&mut data.resizable, "Resizable columns");
|
||||
});
|
||||
|
||||
ui.label("Table type:");
|
||||
ui.radio_value(&mut data.demo, DemoType::Manual, "Few, manual rows");
|
||||
ui.radio_value(
|
||||
&mut data.demo,
|
||||
DemoType::ManyHomogeneous,
|
||||
"Thousands of rows of same height",
|
||||
);
|
||||
ui.radio_value(
|
||||
&mut data.demo,
|
||||
DemoType::ManyHeterogenous,
|
||||
"Thousands of rows of differing heights",
|
||||
);
|
||||
|
||||
if data.demo != DemoType::Manual {
|
||||
ui.add(
|
||||
egui::Slider::new(&mut data.num_rows, 0..=100_000)
|
||||
.logarithmic(true)
|
||||
.text("Num rows"),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let max_rows = if data.demo == DemoType::Manual {
|
||||
NUM_MANUAL_ROWS
|
||||
} else {
|
||||
data.num_rows
|
||||
};
|
||||
|
||||
let slider_response = ui.add(
|
||||
egui::Slider::new(&mut data.scroll_to_row_slider, 0..=max_rows)
|
||||
.logarithmic(true)
|
||||
.text("Row to scroll to"),
|
||||
);
|
||||
if slider_response.changed() {
|
||||
data.scroll_to_row = Some(data.scroll_to_row_slider);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ui.label("Table type:");
|
||||
ui.radio_value(&mut self.demo, DemoType::Manual, "Few, manual rows");
|
||||
ui.radio_value(
|
||||
&mut self.demo,
|
||||
DemoType::ManyHomogeneous,
|
||||
"Thousands of rows of same height",
|
||||
);
|
||||
ui.radio_value(
|
||||
&mut self.demo,
|
||||
DemoType::ManyHeterogenous,
|
||||
"Thousands of rows of differing heights",
|
||||
);
|
||||
|
||||
if self.demo != DemoType::Manual {
|
||||
ui.add(
|
||||
egui::Slider::new(&mut self.num_rows, 0..=100_000)
|
||||
.logarithmic(true)
|
||||
.text("Num rows"),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let max_rows = if self.demo == DemoType::Manual {
|
||||
NUM_MANUAL_ROWS
|
||||
} else {
|
||||
self.num_rows
|
||||
};
|
||||
|
||||
let slider_response = ui.add(
|
||||
egui::Slider::new(&mut self.scroll_to_row_slider, 0..=max_rows)
|
||||
.logarithmic(true)
|
||||
.text("Row to scroll to"),
|
||||
);
|
||||
if slider_response.changed() {
|
||||
self.scroll_to_row = Some(self.scroll_to_row_slider);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
// Leave room for the source code link after the table demo:
|
||||
use egui_extras::{Size, StripBuilder};
|
||||
@@ -123,10 +135,11 @@ impl TableDemo {
|
||||
use egui_extras::{Column, TableBuilder};
|
||||
|
||||
let text_height = egui::TextStyle::Body.resolve(ui.style()).size;
|
||||
let mut data = self.data.write().unwrap();
|
||||
|
||||
let mut table = TableBuilder::new(ui)
|
||||
.striped(self.striped)
|
||||
.resizable(self.resizable)
|
||||
.striped(data.striped)
|
||||
.resizable(data.resizable)
|
||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
||||
.column(Column::auto())
|
||||
.column(Column::initial(100.0).range(40.0..=300.0))
|
||||
@@ -134,7 +147,7 @@ impl TableDemo {
|
||||
.column(Column::remainder())
|
||||
.min_scrolled_height(0.0);
|
||||
|
||||
if let Some(row_nr) = self.scroll_to_row.take() {
|
||||
if let Some(row_nr) = data.scroll_to_row.take() {
|
||||
table = table.scroll_to_row(row_nr, None);
|
||||
}
|
||||
|
||||
@@ -153,7 +166,7 @@ impl TableDemo {
|
||||
ui.strong("Content");
|
||||
});
|
||||
})
|
||||
.body(|mut body| match self.demo {
|
||||
.body(|mut body| match data.demo {
|
||||
DemoType::Manual => {
|
||||
for row_index in 0..NUM_MANUAL_ROWS {
|
||||
let is_thick = thick_row(row_index);
|
||||
@@ -180,7 +193,7 @@ impl TableDemo {
|
||||
}
|
||||
}
|
||||
DemoType::ManyHomogeneous => {
|
||||
body.rows(text_height, self.num_rows, |row_index, mut row| {
|
||||
body.rows(text_height, data.num_rows, |row_index, mut row| {
|
||||
row.col(|ui| {
|
||||
ui.label(row_index.to_string());
|
||||
});
|
||||
@@ -206,7 +219,7 @@ impl TableDemo {
|
||||
}
|
||||
}
|
||||
body.heterogeneous_rows(
|
||||
(0..self.num_rows).map(row_thickness),
|
||||
(0..data.num_rows).map(row_thickness),
|
||||
|row_index, mut row| {
|
||||
row.col(|ui| {
|
||||
ui.label(row_index.to_string());
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CursorTest {}
|
||||
|
||||
@@ -9,7 +11,7 @@ impl super::Demo for CursorTest {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name()).open(open).show(ctx, |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
Self::default().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -41,7 +43,7 @@ impl super::Demo for IdTest {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name()).open(open).show(ctx, |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
Self::default().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -94,15 +96,14 @@ enum WidgetType {
|
||||
TextEdit,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ManualLayoutTest {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct ManualLayoutTestData {
|
||||
widget_offset: egui::Vec2,
|
||||
widget_size: egui::Vec2,
|
||||
widget_type: WidgetType,
|
||||
text_edit_contents: String,
|
||||
}
|
||||
|
||||
impl Default for ManualLayoutTest {
|
||||
impl Default for ManualLayoutTestData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
widget_offset: egui::Vec2::splat(150.0),
|
||||
@@ -113,18 +114,30 @@ impl Default for ManualLayoutTest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ManualLayoutTest {
|
||||
data: Arc<RwLock<ManualLayoutTestData>>,
|
||||
}
|
||||
|
||||
impl PartialEq for ManualLayoutTest {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.data.read().unwrap() == *other.data.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Demo for ManualLayoutTest {
|
||||
fn name(&self) -> &'static str {
|
||||
"Manual Layout Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.resizable(false)
|
||||
.open(open)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -133,12 +146,12 @@ impl super::View for ManualLayoutTest {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
egui::reset_button(ui, self);
|
||||
|
||||
let Self {
|
||||
let ManualLayoutTestData {
|
||||
widget_offset,
|
||||
widget_size,
|
||||
widget_type,
|
||||
text_edit_contents,
|
||||
} = self;
|
||||
} = &mut *self.data.write().unwrap();
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Test widget:");
|
||||
ui.radio_value(widget_type, WidgetType::Button, "Button");
|
||||
@@ -178,9 +191,8 @@ impl super::View for ManualLayoutTest {
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct TableTest {
|
||||
pub struct TableTestData {
|
||||
num_cols: usize,
|
||||
num_rows: usize,
|
||||
min_col_width: f32,
|
||||
@@ -188,7 +200,18 @@ pub struct TableTest {
|
||||
text_length: usize,
|
||||
}
|
||||
|
||||
impl Default for TableTest {
|
||||
#[derive(Default, Clone)]
|
||||
pub struct TableTest {
|
||||
data: Arc<RwLock<TableTestData>>,
|
||||
}
|
||||
|
||||
impl PartialEq for TableTest {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.data.read().unwrap() == *other.data.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TableTestData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
num_cols: 4,
|
||||
@@ -206,96 +229,104 @@ impl super::Demo for TableTest {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name()).open(open).show(ctx, |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
});
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for TableTest {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.add(
|
||||
egui::Slider::new(&mut self.min_col_width, 0.0..=400.0).text("Minimum column width"),
|
||||
);
|
||||
ui.add(
|
||||
egui::Slider::new(&mut self.max_col_width, 0.0..=400.0).text("Maximum column width"),
|
||||
);
|
||||
ui.add(egui::Slider::new(&mut self.num_cols, 0..=5).text("Columns"));
|
||||
ui.add(egui::Slider::new(&mut self.num_rows, 0..=20).text("Rows"));
|
||||
{
|
||||
let mut data = self.data.write().unwrap();
|
||||
ui.add(
|
||||
egui::Slider::new(&mut data.min_col_width, 0.0..=400.0)
|
||||
.text("Minimum column width"),
|
||||
);
|
||||
ui.add(
|
||||
egui::Slider::new(&mut data.max_col_width, 0.0..=400.0)
|
||||
.text("Maximum column width"),
|
||||
);
|
||||
ui.add(egui::Slider::new(&mut data.num_cols, 0..=5).text("Columns"));
|
||||
ui.add(egui::Slider::new(&mut data.num_rows, 0..=20).text("Rows"));
|
||||
|
||||
ui.separator();
|
||||
ui.separator();
|
||||
|
||||
let words = [
|
||||
"random", "words", "in", "a", "random", "order", "that", "just", "keeps", "going",
|
||||
"with", "some", "more",
|
||||
];
|
||||
let words = [
|
||||
"random", "words", "in", "a", "random", "order", "that", "just", "keeps", "going",
|
||||
"with", "some", "more",
|
||||
];
|
||||
|
||||
egui::Grid::new("my_grid")
|
||||
.striped(true)
|
||||
.min_col_width(self.min_col_width)
|
||||
.max_col_width(self.max_col_width)
|
||||
.show(ui, |ui| {
|
||||
for row in 0..self.num_rows {
|
||||
for col in 0..self.num_cols {
|
||||
if col == 0 {
|
||||
ui.label(format!("row {}", row));
|
||||
} else {
|
||||
let word_idx = row * 3 + col * 5;
|
||||
let word_count = (row * 5 + col * 75) % 13;
|
||||
let mut string = String::new();
|
||||
for word in words.iter().cycle().skip(word_idx).take(word_count) {
|
||||
string += word;
|
||||
string += " ";
|
||||
egui::Grid::new("my_grid")
|
||||
.striped(true)
|
||||
.min_col_width(data.min_col_width)
|
||||
.max_col_width(data.max_col_width)
|
||||
.show(ui, |ui| {
|
||||
for row in 0..data.num_rows {
|
||||
for col in 0..data.num_cols {
|
||||
if col == 0 {
|
||||
ui.label(format!("row {}", row));
|
||||
} else {
|
||||
let word_idx = row * 3 + col * 5;
|
||||
let word_count = (row * 5 + col * 75) % 13;
|
||||
let mut string = String::new();
|
||||
for word in words.iter().cycle().skip(word_idx).take(word_count) {
|
||||
string += word;
|
||||
string += " ";
|
||||
}
|
||||
ui.label(string);
|
||||
}
|
||||
ui.label(string);
|
||||
}
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
ui.add(egui::Slider::new(&mut data.text_length, 1..=40).text("Text length"));
|
||||
egui::Grid::new("parent grid").striped(true).show(ui, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.label("Vertical nest1");
|
||||
ui.label("Vertical nest2");
|
||||
});
|
||||
ui.label("First row, second column");
|
||||
ui.end_row();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Horizontal nest1");
|
||||
ui.label("Horizontal nest2");
|
||||
});
|
||||
ui.label("Second row, second column");
|
||||
ui.end_row();
|
||||
|
||||
ui.scope(|ui| {
|
||||
ui.label("Scope nest 1");
|
||||
ui.label("Scope nest 2");
|
||||
});
|
||||
ui.label("Third row, second column");
|
||||
ui.end_row();
|
||||
|
||||
egui::Grid::new("nested grid").show(ui, |ui| {
|
||||
ui.label("Grid nest11");
|
||||
ui.label("Grid nest12");
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
ui.add(egui::Slider::new(&mut self.text_length, 1..=40).text("Text length"));
|
||||
egui::Grid::new("parent grid").striped(true).show(ui, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.label("Vertical nest1");
|
||||
ui.label("Vertical nest2");
|
||||
});
|
||||
ui.label("First row, second column");
|
||||
ui.end_row();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Horizontal nest1");
|
||||
ui.label("Horizontal nest2");
|
||||
});
|
||||
ui.label("Second row, second column");
|
||||
ui.end_row();
|
||||
|
||||
ui.scope(|ui| {
|
||||
ui.label("Scope nest 1");
|
||||
ui.label("Scope nest 2");
|
||||
});
|
||||
ui.label("Third row, second column");
|
||||
ui.end_row();
|
||||
|
||||
egui::Grid::new("nested grid").show(ui, |ui| {
|
||||
ui.label("Grid nest11");
|
||||
ui.label("Grid nest12");
|
||||
ui.label("Grid nest21");
|
||||
ui.label("Grid nest22");
|
||||
ui.end_row();
|
||||
});
|
||||
ui.label("Fourth row, second column");
|
||||
ui.end_row();
|
||||
ui.label("Grid nest21");
|
||||
ui.label("Grid nest22");
|
||||
|
||||
let mut dyn_text = String::from("O");
|
||||
dyn_text.extend(std::iter::repeat('h').take(data.text_length));
|
||||
ui.label(dyn_text);
|
||||
ui.label("Fifth row, second column");
|
||||
ui.end_row();
|
||||
});
|
||||
ui.label("Fourth row, second column");
|
||||
ui.end_row();
|
||||
|
||||
let mut dyn_text = String::from("O");
|
||||
dyn_text.extend(std::iter::repeat('h').take(self.text_length));
|
||||
ui.label(dyn_text);
|
||||
ui.label("Fifth row, second column");
|
||||
ui.end_row();
|
||||
});
|
||||
}
|
||||
|
||||
ui.vertical_centered(|ui| {
|
||||
egui::reset_button(ui, self);
|
||||
@@ -307,9 +338,9 @@ impl super::View for TableTest {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct InputTest {
|
||||
info: String,
|
||||
info: Arc<RwLock<String>>,
|
||||
}
|
||||
|
||||
impl super::Demo for InputTest {
|
||||
@@ -318,12 +349,13 @@ impl super::Demo for InputTest {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -369,23 +401,24 @@ impl super::View for InputTest {
|
||||
}
|
||||
}
|
||||
if !new_info.is_empty() {
|
||||
self.info = new_info;
|
||||
*self.info.write().unwrap() = new_info;
|
||||
}
|
||||
|
||||
ui.label(&self.info);
|
||||
ui.label(&*self.info.write().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WindowResizeTest {
|
||||
text: String,
|
||||
text: Arc<RwLock<String>>,
|
||||
}
|
||||
|
||||
impl Default for WindowResizeTest {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
text: crate::LOREM_IPSUM_LONG.to_owned(),
|
||||
text: Arc::new(RwLock::new(crate::LOREM_IPSUM_LONG.to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -451,14 +484,17 @@ impl super::Demo for WindowResizeTest {
|
||||
lorem_ipsum(ui, crate::LOREM_IPSUM);
|
||||
});
|
||||
|
||||
let clone = self.clone();
|
||||
|
||||
Window::new("↔ resizable with TextEdit")
|
||||
.open(open)
|
||||
.vscroll(false)
|
||||
.resizable(true)
|
||||
.default_height(300.0)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
ui.label("Shows how you can fill an area with a widget.");
|
||||
ui.add_sized(ui.available_size(), TextEdit::multiline(&mut self.text));
|
||||
let mut text = clone.text.write().unwrap();
|
||||
ui.add_sized(ui.available_size(), TextEdit::multiline(&mut *text));
|
||||
});
|
||||
|
||||
Window::new("↔ freely resized")
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// Showcase [`TextEdit`].
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Clone)]
|
||||
pub struct TextEdit {
|
||||
pub text: String,
|
||||
pub text: Arc<RwLock<String>>,
|
||||
}
|
||||
|
||||
impl PartialEq for TextEdit {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.text.read().unwrap() == *other.text.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TextEdit {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
text: "Edit this text".to_owned(),
|
||||
text: Arc::new(RwLock::new("Edit this text".to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,12 +28,13 @@ impl super::Demo for TextEdit {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -33,6 +42,7 @@ impl super::Demo for TextEdit {
|
||||
impl super::View for TextEdit {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
let Self { text } = self;
|
||||
let text = &mut *text.write().unwrap();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
enum Enum {
|
||||
@@ -6,9 +8,8 @@ enum Enum {
|
||||
Third,
|
||||
}
|
||||
|
||||
/// Shows off one example of each major type of widget.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct WidgetGallery {
|
||||
pub struct WidgetGalleryData {
|
||||
enabled: bool,
|
||||
visible: bool,
|
||||
boolean: bool,
|
||||
@@ -26,7 +27,14 @@ pub struct WidgetGallery {
|
||||
texture: Option<egui::TextureHandle>,
|
||||
}
|
||||
|
||||
impl Default for WidgetGallery {
|
||||
/// Shows off one example of each major type of widget.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct WidgetGallery {
|
||||
data: Arc<RwLock<WidgetGalleryData>>,
|
||||
}
|
||||
|
||||
impl Default for WidgetGalleryData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
@@ -50,21 +58,24 @@ impl super::Demo for WidgetGallery {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let clone = self.clone();
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(true)
|
||||
.default_width(280.0)
|
||||
.show(ctx, |ui| {
|
||||
.show(ctx, move |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
clone.clone().ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for WidgetGallery {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.add_enabled_ui(self.enabled, |ui| {
|
||||
ui.set_visible(self.visible);
|
||||
let enabled = self.data.read().unwrap().enabled;
|
||||
let visible = self.data.read().unwrap().visible;
|
||||
ui.add_enabled_ui(enabled, |ui| {
|
||||
ui.set_visible(visible);
|
||||
|
||||
egui::Grid::new("my_grid")
|
||||
.num_columns(2)
|
||||
@@ -74,14 +85,14 @@ impl super::View for WidgetGallery {
|
||||
self.gallery_grid_contents(ui);
|
||||
});
|
||||
});
|
||||
|
||||
let data = &mut *self.data.write().unwrap();
|
||||
ui.separator();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.checkbox(&mut self.visible, "Visible")
|
||||
ui.checkbox(&mut data.visible, "Visible")
|
||||
.on_hover_text("Uncheck to hide all the widgets.");
|
||||
if self.visible {
|
||||
ui.checkbox(&mut self.enabled, "Interactive")
|
||||
if data.visible {
|
||||
ui.checkbox(&mut data.enabled, "Interactive")
|
||||
.on_hover_text("Uncheck to inspect how the widgets look when disabled.");
|
||||
}
|
||||
});
|
||||
@@ -100,7 +111,7 @@ impl super::View for WidgetGallery {
|
||||
|
||||
impl WidgetGallery {
|
||||
fn gallery_grid_contents(&mut self, ui: &mut egui::Ui) {
|
||||
let Self {
|
||||
let WidgetGalleryData {
|
||||
enabled: _,
|
||||
visible: _,
|
||||
boolean,
|
||||
@@ -112,7 +123,7 @@ impl WidgetGallery {
|
||||
#[cfg(feature = "chrono")]
|
||||
date,
|
||||
texture,
|
||||
} = self;
|
||||
} = &mut *self.data.write().unwrap();
|
||||
|
||||
let texture: &egui::TextureHandle = texture.get_or_insert_with(|| {
|
||||
ui.ctx()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct WindowOptions {
|
||||
pub struct WindowOptionsData {
|
||||
title: String,
|
||||
title_bar: bool,
|
||||
closable: bool,
|
||||
@@ -13,8 +15,19 @@ pub struct WindowOptions {
|
||||
anchor: egui::Align2,
|
||||
anchor_offset: egui::Vec2,
|
||||
}
|
||||
#[derive(Clone, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct WindowOptions {
|
||||
data: Arc<RwLock<WindowOptionsData>>,
|
||||
}
|
||||
|
||||
impl Default for WindowOptions {
|
||||
impl PartialEq for WindowOptions {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.data.read().unwrap() == *other.data.read().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WindowOptionsData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
title: "🗖 Window Options".to_owned(),
|
||||
@@ -37,7 +50,7 @@ impl super::Demo for WindowOptions {
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let Self {
|
||||
let WindowOptionsData {
|
||||
title,
|
||||
title_bar,
|
||||
closable,
|
||||
@@ -48,7 +61,7 @@ impl super::Demo for WindowOptions {
|
||||
anchored,
|
||||
anchor,
|
||||
anchor_offset,
|
||||
} = self.clone();
|
||||
} = self.data.read().unwrap().clone();
|
||||
|
||||
let enabled = ctx.input(|i| i.time) - disabled_time > 2.0;
|
||||
if !enabled {
|
||||
@@ -69,70 +82,76 @@ impl super::Demo for WindowOptions {
|
||||
if anchored {
|
||||
window = window.anchor(anchor, anchor_offset);
|
||||
}
|
||||
window.show(ctx, |ui| self.ui(ui));
|
||||
let clone = self.clone();
|
||||
window.show(ctx, move |ui| {
|
||||
let mut clone = clone.clone();
|
||||
clone.ui(ui)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for WindowOptions {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
let Self {
|
||||
title,
|
||||
title_bar,
|
||||
closable,
|
||||
collapsible,
|
||||
resizable,
|
||||
scroll2,
|
||||
disabled_time: _,
|
||||
anchored,
|
||||
anchor,
|
||||
anchor_offset,
|
||||
} = self;
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("title:");
|
||||
ui.text_edit_singleline(title);
|
||||
});
|
||||
{
|
||||
let WindowOptionsData {
|
||||
title,
|
||||
title_bar,
|
||||
closable,
|
||||
collapsible,
|
||||
resizable,
|
||||
scroll2,
|
||||
disabled_time: _,
|
||||
anchored,
|
||||
anchor,
|
||||
anchor_offset,
|
||||
} = &mut *self.data.write().unwrap();
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("title:");
|
||||
ui.text_edit_singleline(title);
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.checkbox(title_bar, "title_bar");
|
||||
ui.checkbox(closable, "closable");
|
||||
ui.checkbox(collapsible, "collapsible");
|
||||
ui.checkbox(resizable, "resizable");
|
||||
ui.checkbox(&mut scroll2[0], "hscroll");
|
||||
ui.checkbox(&mut scroll2[1], "vscroll");
|
||||
ui.horizontal(|ui| {
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.checkbox(title_bar, "title_bar");
|
||||
ui.checkbox(closable, "closable");
|
||||
ui.checkbox(collapsible, "collapsible");
|
||||
ui.checkbox(resizable, "resizable");
|
||||
ui.checkbox(&mut scroll2[0], "hscroll");
|
||||
ui.checkbox(&mut scroll2[1], "vscroll");
|
||||
});
|
||||
});
|
||||
});
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.checkbox(anchored, "anchored");
|
||||
ui.set_enabled(*anchored);
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("x:");
|
||||
ui.selectable_value(&mut anchor[0], egui::Align::LEFT, "Left");
|
||||
ui.selectable_value(&mut anchor[0], egui::Align::Center, "Center");
|
||||
ui.selectable_value(&mut anchor[0], egui::Align::RIGHT, "Right");
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("y:");
|
||||
ui.selectable_value(&mut anchor[1], egui::Align::TOP, "Top");
|
||||
ui.selectable_value(&mut anchor[1], egui::Align::Center, "Center");
|
||||
ui.selectable_value(&mut anchor[1], egui::Align::BOTTOM, "Bottom");
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Offset:");
|
||||
ui.add(egui::DragValue::new(&mut anchor_offset.x));
|
||||
ui.add(egui::DragValue::new(&mut anchor_offset.y));
|
||||
ui.group(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.checkbox(anchored, "anchored");
|
||||
ui.set_enabled(*anchored);
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("x:");
|
||||
ui.selectable_value(&mut anchor[0], egui::Align::LEFT, "Left");
|
||||
ui.selectable_value(&mut anchor[0], egui::Align::Center, "Center");
|
||||
ui.selectable_value(&mut anchor[0], egui::Align::RIGHT, "Right");
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("y:");
|
||||
ui.selectable_value(&mut anchor[1], egui::Align::TOP, "Top");
|
||||
ui.selectable_value(&mut anchor[1], egui::Align::Center, "Center");
|
||||
ui.selectable_value(&mut anchor[1], egui::Align::BOTTOM, "Bottom");
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Offset:");
|
||||
ui.add(egui::DragValue::new(&mut anchor_offset.x));
|
||||
ui.add(egui::DragValue::new(&mut anchor_offset.y));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Disable for 2 seconds").clicked() {
|
||||
self.disabled_time = ui.input(|i| i.time);
|
||||
self.data.write().unwrap().disabled_time = ui.input(|i| i.time);
|
||||
}
|
||||
egui::reset_button(ui, self);
|
||||
ui.add(crate::egui_github_link_file!());
|
||||
|
||||
@@ -14,7 +14,7 @@ impl super::Demo for WindowWithPanels {
|
||||
.default_height(400.0)
|
||||
.vscroll(false)
|
||||
.open(open);
|
||||
window.show(ctx, |ui| self.ui(ui));
|
||||
window.show(ctx, |ui| Self {}.ui(ui));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use eframe::egui;
|
||||
|
||||
fn main() -> Result<(), eframe::Error> {
|
||||
@@ -14,8 +16,8 @@ fn main() -> Result<(), eframe::Error> {
|
||||
let mut name = "Arthur".to_owned();
|
||||
let mut age = 42;
|
||||
|
||||
let mut window1_embedded = true;
|
||||
let mut window2_embedded = true;
|
||||
let mut window1_embedded = Arc::new(RwLock::new(true));
|
||||
let mut window2_embedded = Arc::new(RwLock::new(true));
|
||||
|
||||
eframe::run_simple_native("My egui App", options, move |ctx, _frame| {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
@@ -35,11 +37,14 @@ fn main() -> Result<(), eframe::Error> {
|
||||
age += 1;
|
||||
}
|
||||
ui.label(format!("Hello '{name}', age {age}"));
|
||||
let clone = window1_embedded.clone();
|
||||
let embedded = *window1_embedded.read().unwrap();
|
||||
egui::CollapsingHeader::new("Show Test1").show(ui, |ui| {
|
||||
egui::Window::new("Test1")
|
||||
.embedded(window1_embedded)
|
||||
.show(ctx, |ui| {
|
||||
ui.checkbox(&mut window1_embedded, "Should embedd?");
|
||||
.embedded(embedded)
|
||||
.show(ctx, move |ui| {
|
||||
ui.checkbox(&mut *clone.write().unwrap(), "Should embedd?");
|
||||
let ctx = ui.ctx().clone();
|
||||
ui.label(format!(
|
||||
"Current window: {}, Current rendering window: {}",
|
||||
ctx.current_viewport(),
|
||||
@@ -47,11 +52,14 @@ fn main() -> Result<(), eframe::Error> {
|
||||
));
|
||||
});
|
||||
});
|
||||
let clone = window2_embedded.clone();
|
||||
let embedded = *window2_embedded.read().unwrap();
|
||||
egui::CollapsingHeader::new("Shout Test2").show(ui, |ui| {
|
||||
egui::Window::new("Test2")
|
||||
.embedded(window2_embedded)
|
||||
.show(ctx, |ui| {
|
||||
ui.checkbox(&mut window2_embedded, "Should embedd?");
|
||||
.embedded(embedded)
|
||||
.show(ctx, move |ui| {
|
||||
ui.checkbox(&mut *clone.write().unwrap(), "Should embedd?");
|
||||
let ctx = ui.ctx().clone();
|
||||
ui.label(format!(
|
||||
"Current window: {}, Current rendering window: {}",
|
||||
ctx.current_viewport(),
|
||||
|
||||
Reference in New Issue
Block a user