mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 05:10:03 -04:00
Merge remote-tracking branch 'egui/master' into dynamic-grid
This commit is contained in:
@@ -18,22 +18,43 @@ all-features = true
|
||||
|
||||
[lib]
|
||||
|
||||
|
||||
[features]
|
||||
default = ["datetime"]
|
||||
|
||||
# Enable additional checks if debug assertions are enabled (debug builds).
|
||||
extra_debug_asserts = ["egui/extra_debug_asserts"]
|
||||
# Always enable additional checks.
|
||||
extra_asserts = ["egui/extra_asserts"]
|
||||
|
||||
datetime = ["egui_extras/chrono", "chrono"]
|
||||
http = ["ehttp", "image", "poll-promise"]
|
||||
persistence = [
|
||||
"egui/persistence",
|
||||
"epi/persistence",
|
||||
"egui_extras/persistence",
|
||||
"serde",
|
||||
]
|
||||
serialize = ["egui/serialize", "serde"]
|
||||
syntax_highlighting = ["syntect"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
egui = { version = "0.16.0", path = "../egui", default-features = false }
|
||||
epi = { version = "0.16.0", path = "../epi" }
|
||||
egui_extras = { version = "0.16.0", path = "../egui_extras" }
|
||||
|
||||
chrono = { version = "0.4", features = ["js-sys", "wasmbind"], optional = true }
|
||||
enum-map = { version = "1", features = ["serde"] }
|
||||
enum-map = { version = "2", features = ["serde"] }
|
||||
unicode_names2 = { version = "0.4.0", default-features = false }
|
||||
|
||||
# feature "http":
|
||||
ehttp = { version = "0.1.0", optional = true }
|
||||
image = { version = "0.23", default-features = false, features = [
|
||||
ehttp = { version = "0.2.0", optional = true }
|
||||
image = { version = "0.24", default-features = false, features = [
|
||||
"jpeg",
|
||||
"png",
|
||||
], optional = true }
|
||||
|
||||
poll-promise = { version = "0.1", default-features = false, optional = true }
|
||||
# feature "syntax_highlighting":
|
||||
syntect = { version = "4", default-features = false, features = [
|
||||
"default-fancy",
|
||||
@@ -45,25 +66,6 @@ serde = { version = "1", features = ["derive"], optional = true }
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.3", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["datetime"]
|
||||
datetime = ["egui_extras/datetime", "chrono"]
|
||||
|
||||
# Enable additional checks if debug assertions are enabled (debug builds).
|
||||
extra_debug_asserts = ["egui/extra_debug_asserts"]
|
||||
# Always enable additional checks.
|
||||
extra_asserts = ["egui/extra_asserts"]
|
||||
|
||||
http = ["ehttp", "image"]
|
||||
persistence = [
|
||||
"egui/persistence",
|
||||
"epi/persistence",
|
||||
"egui_extras/persistence",
|
||||
"serde",
|
||||
]
|
||||
serialize = ["egui/serialize", "serde"]
|
||||
syntax_highlighting = ["syntect"]
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
|
||||
@@ -4,16 +4,16 @@ use egui::epaint::TextShape;
|
||||
use egui_demo_lib::LOREM_IPSUM_LONG;
|
||||
|
||||
pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
let raw_input = egui::RawInput::default();
|
||||
use egui::RawInput;
|
||||
|
||||
{
|
||||
let mut ctx = egui::CtxRef::default();
|
||||
let ctx = egui::Context::default();
|
||||
let mut demo_windows = egui_demo_lib::DemoWindows::default();
|
||||
|
||||
// The most end-to-end benchmark.
|
||||
c.bench_function("demo_with_tessellate__realistic", |b| {
|
||||
b.iter(|| {
|
||||
let (_output, shapes) = ctx.run(raw_input.clone(), |ctx| {
|
||||
let (_output, shapes) = ctx.run(RawInput::default(), |ctx| {
|
||||
demo_windows.ui(ctx);
|
||||
});
|
||||
ctx.tessellate(shapes)
|
||||
@@ -22,13 +22,13 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
||||
c.bench_function("demo_no_tessellate", |b| {
|
||||
b.iter(|| {
|
||||
ctx.run(raw_input.clone(), |ctx| {
|
||||
ctx.run(RawInput::default(), |ctx| {
|
||||
demo_windows.ui(ctx);
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
let (_output, shapes) = ctx.run(raw_input.clone(), |ctx| {
|
||||
let (_output, shapes) = ctx.run(RawInput::default(), |ctx| {
|
||||
demo_windows.ui(ctx);
|
||||
});
|
||||
c.bench_function("demo_only_tessellate", |b| {
|
||||
@@ -37,12 +37,12 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
if false {
|
||||
let mut ctx = egui::CtxRef::default();
|
||||
let ctx = egui::Context::default();
|
||||
ctx.memory().set_everything_is_visible(true); // give us everything
|
||||
let mut demo_windows = egui_demo_lib::DemoWindows::default();
|
||||
c.bench_function("demo_full_no_tessellate", |b| {
|
||||
b.iter(|| {
|
||||
ctx.run(raw_input.clone(), |ctx| {
|
||||
ctx.run(RawInput::default(), |ctx| {
|
||||
demo_windows.ui(ctx);
|
||||
})
|
||||
})
|
||||
@@ -50,8 +50,8 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
{
|
||||
let mut ctx = egui::CtxRef::default();
|
||||
let _ = ctx.run(raw_input, |ctx| {
|
||||
let ctx = egui::Context::default();
|
||||
let _ = ctx.run(RawInput::default(), |ctx| {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
c.bench_function("label &str", |b| {
|
||||
b.iter(|| {
|
||||
@@ -68,40 +68,68 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
{
|
||||
let pixels_per_point = 1.0;
|
||||
let wrap_width = 512.0;
|
||||
let text_style = egui::TextStyle::Body;
|
||||
let color = egui::Color32::WHITE;
|
||||
let fonts =
|
||||
egui::epaint::text::Fonts::new(pixels_per_point, egui::FontDefinitions::default());
|
||||
c.bench_function("text_layout_uncached", |b| {
|
||||
b.iter(|| {
|
||||
use egui::epaint::text::{layout, LayoutJob};
|
||||
let ctx = egui::Context::default();
|
||||
ctx.begin_frame(RawInput::default());
|
||||
|
||||
let job = LayoutJob::simple(
|
||||
egui::CentralPanel::default().show(&ctx, |ui| {
|
||||
c.bench_function("Painter::rect", |b| {
|
||||
let painter = ui.painter();
|
||||
let rect = ui.max_rect();
|
||||
b.iter(|| {
|
||||
painter.rect(rect, 2.0, egui::Color32::RED, (1.0, egui::Color32::WHITE));
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// Don't call `end_frame` to not have to drain the huge paint list
|
||||
}
|
||||
|
||||
{
|
||||
let pixels_per_point = 1.0;
|
||||
let max_texture_side = 8 * 1024;
|
||||
let wrap_width = 512.0;
|
||||
let font_id = egui::FontId::default();
|
||||
let color = egui::Color32::WHITE;
|
||||
let fonts = egui::epaint::text::Fonts::new(
|
||||
pixels_per_point,
|
||||
max_texture_side,
|
||||
egui::FontDefinitions::default(),
|
||||
);
|
||||
{
|
||||
let mut locked_fonts = fonts.lock();
|
||||
c.bench_function("text_layout_uncached", |b| {
|
||||
b.iter(|| {
|
||||
use egui::epaint::text::{layout, LayoutJob};
|
||||
|
||||
let job = LayoutJob::simple(
|
||||
LOREM_IPSUM_LONG.to_owned(),
|
||||
font_id.clone(),
|
||||
color,
|
||||
wrap_width,
|
||||
);
|
||||
layout(&mut locked_fonts.fonts, job.into())
|
||||
})
|
||||
});
|
||||
}
|
||||
c.bench_function("text_layout_cached", |b| {
|
||||
b.iter(|| {
|
||||
fonts.layout(
|
||||
LOREM_IPSUM_LONG.to_owned(),
|
||||
egui::TextStyle::Body,
|
||||
font_id.clone(),
|
||||
color,
|
||||
wrap_width,
|
||||
);
|
||||
layout(&fonts, job.into())
|
||||
)
|
||||
})
|
||||
});
|
||||
c.bench_function("text_layout_cached", |b| {
|
||||
b.iter(|| fonts.layout(LOREM_IPSUM_LONG.to_owned(), text_style, color, wrap_width))
|
||||
});
|
||||
|
||||
let galley = fonts.layout(LOREM_IPSUM_LONG.to_owned(), text_style, color, wrap_width);
|
||||
let galley = fonts.layout(LOREM_IPSUM_LONG.to_owned(), font_id, color, wrap_width);
|
||||
let mut tessellator = egui::epaint::Tessellator::from_options(Default::default());
|
||||
let mut mesh = egui::epaint::Mesh::default();
|
||||
let text_shape = TextShape::new(egui::Pos2::ZERO, galley);
|
||||
let font_image_size = fonts.font_image_size();
|
||||
c.bench_function("tessellate_text", |b| {
|
||||
b.iter(|| {
|
||||
tessellator.tessellate_text(
|
||||
fonts.font_image().size(),
|
||||
text_shape.clone(),
|
||||
&mut mesh,
|
||||
);
|
||||
tessellator.tessellate_text(font_image_size, text_shape.clone(), &mut mesh);
|
||||
mesh.clear();
|
||||
})
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ impl epi::App for ColorTest {
|
||||
"🎨 Color test"
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &epi::Frame) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
if frame.is_web() {
|
||||
ui.label(
|
||||
@@ -43,14 +43,14 @@ impl epi::App for ColorTest {
|
||||
ui.separator();
|
||||
}
|
||||
ScrollArea::both().auto_shrink([false; 2]).show(ui, |ui| {
|
||||
self.ui(ui, Some(frame));
|
||||
self.ui(ui);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorTest {
|
||||
pub fn ui(&mut self, ui: &mut Ui, tex_allocator: Option<&dyn epi::TextureAllocator>) {
|
||||
pub fn ui(&mut self, ui: &mut Ui) {
|
||||
ui.set_max_width(680.0);
|
||||
|
||||
ui.vertical_centered(|ui| {
|
||||
@@ -70,13 +70,7 @@ impl ColorTest {
|
||||
ui.spacing_mut().item_spacing.y = 0.0; // No spacing between gradients
|
||||
let g = Gradient::one_color(Color32::from_rgb(255, 165, 0));
|
||||
self.vertex_gradient(ui, "orange rgb(255, 165, 0) - vertex", WHITE, &g);
|
||||
self.tex_gradient(
|
||||
ui,
|
||||
tex_allocator,
|
||||
"orange rgb(255, 165, 0) - texture",
|
||||
WHITE,
|
||||
&g,
|
||||
);
|
||||
self.tex_gradient(ui, "orange rgb(255, 165, 0) - texture", WHITE, &g);
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
@@ -99,20 +93,18 @@ impl ColorTest {
|
||||
{
|
||||
let g = Gradient::one_color(Color32::from(tex_color * vertex_color));
|
||||
self.vertex_gradient(ui, "Ground truth (vertices)", WHITE, &g);
|
||||
self.tex_gradient(ui, tex_allocator, "Ground truth (texture)", WHITE, &g);
|
||||
}
|
||||
if let Some(tex_allocator) = tex_allocator {
|
||||
ui.horizontal(|ui| {
|
||||
let g = Gradient::one_color(Color32::from(tex_color));
|
||||
let tex = self.tex_mngr.get(tex_allocator, &g);
|
||||
let texel_offset = 0.5 / (g.0.len() as f32);
|
||||
let uv =
|
||||
Rect::from_min_max(pos2(texel_offset, 0.0), pos2(1.0 - texel_offset, 1.0));
|
||||
ui.add(Image::new(tex, GRADIENT_SIZE).tint(vertex_color).uv(uv))
|
||||
.on_hover_text(format!("A texture that is {} texels wide", g.0.len()));
|
||||
ui.label("GPU result");
|
||||
});
|
||||
self.tex_gradient(ui, "Ground truth (texture)", WHITE, &g);
|
||||
}
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
let g = Gradient::one_color(Color32::from(tex_color));
|
||||
let tex = self.tex_mngr.get(ui.ctx(), &g);
|
||||
let texel_offset = 0.5 / (g.0.len() as f32);
|
||||
let uv = Rect::from_min_max(pos2(texel_offset, 0.0), pos2(1.0 - texel_offset, 1.0));
|
||||
ui.add(Image::new(tex, GRADIENT_SIZE).tint(vertex_color).uv(uv))
|
||||
.on_hover_text(format!("A texture that is {} texels wide", g.0.len()));
|
||||
ui.label("GPU result");
|
||||
});
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
@@ -120,18 +112,18 @@ impl ColorTest {
|
||||
// TODO: test color multiplication (image tint),
|
||||
// to make sure vertex and texture color multiplication is done in linear space.
|
||||
|
||||
self.show_gradients(ui, tex_allocator, WHITE, (RED, GREEN));
|
||||
self.show_gradients(ui, WHITE, (RED, GREEN));
|
||||
if self.srgb {
|
||||
ui.label("Notice the darkening in the center of the naive sRGB interpolation.");
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
self.show_gradients(ui, tex_allocator, RED, (TRANSPARENT, GREEN));
|
||||
self.show_gradients(ui, RED, (TRANSPARENT, GREEN));
|
||||
|
||||
ui.separator();
|
||||
|
||||
self.show_gradients(ui, tex_allocator, WHITE, (TRANSPARENT, GREEN));
|
||||
self.show_gradients(ui, WHITE, (TRANSPARENT, GREEN));
|
||||
if self.srgb {
|
||||
ui.label(
|
||||
"Notice how the linear blend stays green while the naive sRGBA interpolation looks gray in the middle.",
|
||||
@@ -142,15 +134,14 @@ impl ColorTest {
|
||||
|
||||
// TODO: another ground truth where we do the alpha-blending against the background also.
|
||||
// TODO: exactly the same thing, but with vertex colors (no textures)
|
||||
self.show_gradients(ui, tex_allocator, WHITE, (TRANSPARENT, BLACK));
|
||||
self.show_gradients(ui, WHITE, (TRANSPARENT, BLACK));
|
||||
ui.separator();
|
||||
self.show_gradients(ui, tex_allocator, BLACK, (TRANSPARENT, WHITE));
|
||||
self.show_gradients(ui, BLACK, (TRANSPARENT, WHITE));
|
||||
ui.separator();
|
||||
|
||||
ui.label("Additive blending: add more and more blue to the red background:");
|
||||
self.show_gradients(
|
||||
ui,
|
||||
tex_allocator,
|
||||
RED,
|
||||
(TRANSPARENT, Color32::from_rgb_additive(0, 0, 255)),
|
||||
);
|
||||
@@ -160,13 +151,7 @@ impl ColorTest {
|
||||
pixel_test(ui);
|
||||
}
|
||||
|
||||
fn show_gradients(
|
||||
&mut self,
|
||||
ui: &mut Ui,
|
||||
tex_allocator: Option<&dyn epi::TextureAllocator>,
|
||||
bg_fill: Color32,
|
||||
(left, right): (Color32, Color32),
|
||||
) {
|
||||
fn show_gradients(&mut self, ui: &mut Ui, bg_fill: Color32, (left, right): (Color32, Color32)) {
|
||||
let is_opaque = left.is_opaque() && right.is_opaque();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
@@ -186,13 +171,7 @@ impl ColorTest {
|
||||
if is_opaque {
|
||||
let g = Gradient::ground_truth_linear_gradient(left, right);
|
||||
self.vertex_gradient(ui, "Ground Truth (CPU gradient) - vertices", bg_fill, &g);
|
||||
self.tex_gradient(
|
||||
ui,
|
||||
tex_allocator,
|
||||
"Ground Truth (CPU gradient) - texture",
|
||||
bg_fill,
|
||||
&g,
|
||||
);
|
||||
self.tex_gradient(ui, "Ground Truth (CPU gradient) - texture", bg_fill, &g);
|
||||
} else {
|
||||
let g = Gradient::ground_truth_linear_gradient(left, right).with_bg_fill(bg_fill);
|
||||
self.vertex_gradient(
|
||||
@@ -203,20 +182,13 @@ impl ColorTest {
|
||||
);
|
||||
self.tex_gradient(
|
||||
ui,
|
||||
tex_allocator,
|
||||
"Ground Truth (CPU gradient, CPU blending) - texture",
|
||||
bg_fill,
|
||||
&g,
|
||||
);
|
||||
let g = Gradient::ground_truth_linear_gradient(left, right);
|
||||
self.vertex_gradient(ui, "CPU gradient, GPU blending - vertices", bg_fill, &g);
|
||||
self.tex_gradient(
|
||||
ui,
|
||||
tex_allocator,
|
||||
"CPU gradient, GPU blending - texture",
|
||||
bg_fill,
|
||||
&g,
|
||||
);
|
||||
self.tex_gradient(ui, "CPU gradient, GPU blending - texture", bg_fill, &g);
|
||||
}
|
||||
|
||||
let g = Gradient::texture_gradient(left, right);
|
||||
@@ -226,13 +198,7 @@ impl ColorTest {
|
||||
bg_fill,
|
||||
&g,
|
||||
);
|
||||
self.tex_gradient(
|
||||
ui,
|
||||
tex_allocator,
|
||||
"Texture of width 2 (test texture sampler)",
|
||||
bg_fill,
|
||||
&g,
|
||||
);
|
||||
self.tex_gradient(ui, "Texture of width 2 (test texture sampler)", bg_fill, &g);
|
||||
|
||||
if self.srgb {
|
||||
let g =
|
||||
@@ -243,41 +209,26 @@ impl ColorTest {
|
||||
bg_fill,
|
||||
&g,
|
||||
);
|
||||
self.tex_gradient(
|
||||
ui,
|
||||
tex_allocator,
|
||||
"Naive sRGBA interpolation (WRONG)",
|
||||
bg_fill,
|
||||
&g,
|
||||
);
|
||||
self.tex_gradient(ui, "Naive sRGBA interpolation (WRONG)", bg_fill, &g);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn tex_gradient(
|
||||
&mut self,
|
||||
ui: &mut Ui,
|
||||
tex_allocator: Option<&dyn epi::TextureAllocator>,
|
||||
label: &str,
|
||||
bg_fill: Color32,
|
||||
gradient: &Gradient,
|
||||
) {
|
||||
fn tex_gradient(&mut self, ui: &mut Ui, label: &str, bg_fill: Color32, gradient: &Gradient) {
|
||||
if !self.texture_gradients {
|
||||
return;
|
||||
}
|
||||
if let Some(tex_allocator) = tex_allocator {
|
||||
ui.horizontal(|ui| {
|
||||
let tex = self.tex_mngr.get(tex_allocator, gradient);
|
||||
let texel_offset = 0.5 / (gradient.0.len() as f32);
|
||||
let uv = Rect::from_min_max(pos2(texel_offset, 0.0), pos2(1.0 - texel_offset, 1.0));
|
||||
ui.add(Image::new(tex, GRADIENT_SIZE).bg_fill(bg_fill).uv(uv))
|
||||
.on_hover_text(format!(
|
||||
"A texture that is {} texels wide",
|
||||
gradient.0.len()
|
||||
));
|
||||
ui.label(label);
|
||||
});
|
||||
}
|
||||
ui.horizontal(|ui| {
|
||||
let tex = self.tex_mngr.get(ui.ctx(), gradient);
|
||||
let texel_offset = 0.5 / (gradient.0.len() as f32);
|
||||
let uv = Rect::from_min_max(pos2(texel_offset, 0.0), pos2(1.0 - texel_offset, 1.0));
|
||||
ui.add(Image::new(tex, GRADIENT_SIZE).bg_fill(bg_fill).uv(uv))
|
||||
.on_hover_text(format!(
|
||||
"A texture that is {} texels wide",
|
||||
gradient.0.len()
|
||||
));
|
||||
ui.label(label);
|
||||
});
|
||||
}
|
||||
|
||||
fn vertex_gradient(&mut self, ui: &mut Ui, label: &str, bg_fill: Color32, gradient: &Gradient) {
|
||||
@@ -384,18 +335,21 @@ impl Gradient {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TextureManager(HashMap<Gradient, TextureId>);
|
||||
struct TextureManager(HashMap<Gradient, TextureHandle>);
|
||||
|
||||
impl TextureManager {
|
||||
fn get(&mut self, tex_allocator: &dyn epi::TextureAllocator, gradient: &Gradient) -> TextureId {
|
||||
*self.0.entry(gradient.clone()).or_insert_with(|| {
|
||||
fn get(&mut self, ctx: &egui::Context, gradient: &Gradient) -> &TextureHandle {
|
||||
self.0.entry(gradient.clone()).or_insert_with(|| {
|
||||
let pixels = gradient.to_pixel_row();
|
||||
let width = pixels.len();
|
||||
let height = 1;
|
||||
tex_allocator.alloc(epi::Image {
|
||||
size: [width, height],
|
||||
pixels,
|
||||
})
|
||||
ctx.load_texture(
|
||||
"color_test_gradient",
|
||||
epaint::ColorImage {
|
||||
size: [width, height],
|
||||
pixels,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ impl epi::App for DemoApp {
|
||||
|
||||
fn setup(
|
||||
&mut self,
|
||||
_ctx: &egui::CtxRef,
|
||||
_ctx: &egui::Context,
|
||||
_frame: &epi::Frame,
|
||||
_storage: Option<&dyn epi::Storage>,
|
||||
) {
|
||||
@@ -31,7 +31,7 @@ impl epi::App for DemoApp {
|
||||
epi::set_value(storage, epi::APP_KEY, self);
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::CtxRef, _frame: &epi::Frame) {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
|
||||
self.demo_windows.ui(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ impl super::Demo for CodeEditor {
|
||||
"🖮 Code Editor"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
use super::View as _;
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
@@ -71,7 +71,7 @@ impl super::View for CodeEditor {
|
||||
ui.collapsing("Theme", |ui| {
|
||||
ui.group(|ui| {
|
||||
theme.ui(ui);
|
||||
theme.store_in_memory(ui.ctx());
|
||||
theme.clone().store_in_memory(ui.ctx());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ impl super::View for CodeEditor {
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(code)
|
||||
.text_style(egui::TextStyle::Monospace) // for cursor height
|
||||
.font(egui::TextStyle::Monospace) // for cursor height
|
||||
.code_editor()
|
||||
.desired_rows(10)
|
||||
.lock_focus(true)
|
||||
|
||||
@@ -68,7 +68,7 @@ impl super::Demo for CodeExample {
|
||||
"🖮 Code Example"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
use super::View;
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
@@ -98,7 +98,8 @@ impl CodeExample {
|
||||
);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
let indentation = 8.0 * ui.fonts()[egui::TextStyle::Monospace].glyph_width(' ');
|
||||
let font_id = egui::TextStyle::Monospace.resolve(ui.style());
|
||||
let indentation = 8.0 * ui.fonts().glyph_width(&font_id, ' ');
|
||||
let item_spacing = ui.spacing_mut().item_spacing;
|
||||
ui.add_space(indentation - item_spacing.x);
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ impl super::Demo for ContextMenus {
|
||||
"☰ Context Menus"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
use super::View;
|
||||
egui::Window::new(self.name())
|
||||
.vscroll(false)
|
||||
|
||||
@@ -10,7 +10,7 @@ impl super::Demo for DancingStrings {
|
||||
"♫ Dancing Strings"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
use super::View as _;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::Demo;
|
||||
use egui::{CtxRef, ScrollArea, Ui};
|
||||
use egui::{Context, ScrollArea, Ui};
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -26,6 +26,7 @@ impl Default for Demos {
|
||||
Box::new(super::MiscDemoWindow::default()),
|
||||
Box::new(super::multi_touch::MultiTouch::default()),
|
||||
Box::new(super::painting::Painting::default()),
|
||||
Box::new(super::paint_bezier::PaintBezier::default()),
|
||||
Box::new(super::plot_demo::PlotDemo::default()),
|
||||
Box::new(super::scrolling::Scrolling::default()),
|
||||
Box::new(super::sliders::Sliders::default()),
|
||||
@@ -60,7 +61,7 @@ impl Demos {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn windows(&mut self, ctx: &CtxRef) {
|
||||
pub fn windows(&mut self, ctx: &Context) {
|
||||
let Self { demos, open } = self;
|
||||
for demo in demos {
|
||||
let mut is_open = open.contains(demo.name());
|
||||
@@ -115,7 +116,7 @@ impl Tests {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn windows(&mut self, ctx: &CtxRef) {
|
||||
pub fn windows(&mut self, ctx: &Context) {
|
||||
let Self { demos, open } = self;
|
||||
for demo in demos {
|
||||
let mut is_open = open.contains(demo.name());
|
||||
@@ -151,7 +152,7 @@ pub struct DemoWindows {
|
||||
impl DemoWindows {
|
||||
/// Show the app ui (menu bar and windows).
|
||||
/// `sidebar_ui` can be used to optionally show some things in the sidebar
|
||||
pub fn ui(&mut self, ctx: &CtxRef) {
|
||||
pub fn ui(&mut self, ctx: &Context) {
|
||||
let Self { demos, tests } = self;
|
||||
|
||||
egui::SidePanel::right("egui_demo_panel")
|
||||
@@ -216,7 +217,7 @@ impl DemoWindows {
|
||||
}
|
||||
|
||||
/// Show the open windows.
|
||||
fn windows(&mut self, ctx: &CtxRef) {
|
||||
fn windows(&mut self, ctx: &Context) {
|
||||
let Self { demos, tests } = self;
|
||||
|
||||
demos.windows(ctx);
|
||||
|
||||
@@ -25,7 +25,7 @@ pub fn drag_source(ui: &mut Ui, id: Id, body: impl FnOnce(&mut Ui)) {
|
||||
// (anything with `Order::Tooltip` always gets an empty `Response`)
|
||||
// So this is fine!
|
||||
|
||||
if let Some(pointer_pos) = ui.input().pointer.interact_pos() {
|
||||
if let Some(pointer_pos) = ui.ctx().pointer_interact_pos() {
|
||||
let delta = pointer_pos - response.rect.center();
|
||||
ui.ctx().translate_layer(layer_id, delta);
|
||||
}
|
||||
@@ -66,7 +66,7 @@ pub fn drop_target<R>(
|
||||
ui.painter().set(
|
||||
where_to_put_background,
|
||||
epaint::RectShape {
|
||||
corner_radius: style.corner_radius,
|
||||
rounding: style.rounding,
|
||||
fill,
|
||||
stroke,
|
||||
rect,
|
||||
@@ -101,7 +101,7 @@ impl super::Demo for DragAndDropDemo {
|
||||
"✋ Drag and Drop"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
use super::View as _;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
|
||||
@@ -2,15 +2,15 @@ use std::collections::BTreeMap;
|
||||
|
||||
pub struct FontBook {
|
||||
filter: String,
|
||||
text_style: egui::TextStyle,
|
||||
named_chars: BTreeMap<egui::TextStyle, BTreeMap<char, String>>,
|
||||
font_id: egui::FontId,
|
||||
named_chars: BTreeMap<egui::FontFamily, BTreeMap<char, String>>,
|
||||
}
|
||||
|
||||
impl Default for FontBook {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
filter: Default::default(),
|
||||
text_style: egui::TextStyle::Button,
|
||||
font_id: egui::FontId::proportional(20.0),
|
||||
named_chars: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ impl super::Demo for FontBook {
|
||||
"🔤 Font Book"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
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);
|
||||
@@ -34,7 +34,7 @@ impl super::View for FontBook {
|
||||
ui.label(format!(
|
||||
"The selected font supports {} characters.",
|
||||
self.named_chars
|
||||
.get(&self.text_style)
|
||||
.get(&self.font_id.family)
|
||||
.map(|map| map.len())
|
||||
.unwrap_or_default()
|
||||
));
|
||||
@@ -51,13 +51,7 @@ impl super::View for FontBook {
|
||||
|
||||
ui.separator();
|
||||
|
||||
egui::ComboBox::from_label("Text style")
|
||||
.selected_text(format!("{:?}", self.text_style))
|
||||
.show_ui(ui, |ui| {
|
||||
for style in egui::TextStyle::all() {
|
||||
ui.selectable_value(&mut self.text_style, style, format!("{:?}", style));
|
||||
}
|
||||
});
|
||||
egui::introspection::font_id_ui(ui, &mut self.font_id);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Filter:");
|
||||
@@ -68,16 +62,11 @@ impl super::View for FontBook {
|
||||
}
|
||||
});
|
||||
|
||||
let text_style = self.text_style;
|
||||
let filter = &self.filter;
|
||||
let named_chars = self.named_chars.entry(text_style).or_insert_with(|| {
|
||||
ui.fonts()[text_style]
|
||||
.characters()
|
||||
.iter()
|
||||
.filter(|chr| !chr.is_whitespace() && !chr.is_ascii_control())
|
||||
.map(|&chr| (chr, char_name(chr)))
|
||||
.collect()
|
||||
});
|
||||
let named_chars = self
|
||||
.named_chars
|
||||
.entry(self.font_id.family.clone())
|
||||
.or_insert_with(|| available_characters(ui, self.font_id.family.clone()));
|
||||
|
||||
ui.separator();
|
||||
|
||||
@@ -88,12 +77,14 @@ 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()).text_style(text_style),
|
||||
egui::RichText::new(chr.to_string()).font(self.font_id.clone()),
|
||||
)
|
||||
.frame(false);
|
||||
|
||||
let tooltip_ui = |ui: &mut egui::Ui| {
|
||||
ui.label(egui::RichText::new(chr.to_string()).text_style(text_style));
|
||||
ui.label(
|
||||
egui::RichText::new(chr.to_string()).font(self.font_id.clone()),
|
||||
);
|
||||
ui.label(format!("{}\nU+{:X}\n\nClick to copy", name, chr as u32));
|
||||
};
|
||||
|
||||
@@ -107,6 +98,18 @@ impl super::View for FontBook {
|
||||
}
|
||||
}
|
||||
|
||||
fn available_characters(ui: &egui::Ui, family: egui::FontFamily) -> BTreeMap<char, String> {
|
||||
ui.fonts()
|
||||
.lock()
|
||||
.fonts
|
||||
.font(&egui::FontId::new(10.0, family)) // size is arbitrary for getting the characters
|
||||
.characters()
|
||||
.iter()
|
||||
.filter(|chr| !chr.is_whitespace() && !chr.is_ascii_control())
|
||||
.map(|&chr| (chr, char_name(chr)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn char_name(chr: char) -> String {
|
||||
special_char_name(chr)
|
||||
.map(|s| s.to_owned())
|
||||
|
||||
@@ -11,7 +11,7 @@ impl super::Demo for GridDemo {
|
||||
"▣ Grid Demo"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(true)
|
||||
|
||||
@@ -76,7 +76,7 @@ impl super::Demo for LayoutTest {
|
||||
"Layout Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
|
||||
@@ -31,7 +31,7 @@ impl Demo for MiscDemoWindow {
|
||||
"✨ Misc Demos"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
.vscroll(true)
|
||||
@@ -140,7 +140,8 @@ impl Widgets {
|
||||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
// Trick so we don't have to add spaces in the text below:
|
||||
ui.spacing_mut().item_spacing.x = ui.fonts()[TextStyle::Body].glyph_width(' ');
|
||||
let width = ui.fonts().glyph_width(&TextStyle::Body.resolve(ui.style()), ' ');
|
||||
ui.spacing_mut().item_spacing.x = width;
|
||||
|
||||
ui.label(RichText::new("Text can have").color(Color32::from_rgb(110, 255, 110)));
|
||||
ui.colored_label(Color32::from_rgb(128, 140, 255), "color"); // Shortcut version
|
||||
@@ -268,7 +269,7 @@ impl ColorWidgets {
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
struct BoxPainting {
|
||||
size: Vec2,
|
||||
corner_radius: f32,
|
||||
rounding: f32,
|
||||
stroke_width: f32,
|
||||
num_boxes: usize,
|
||||
}
|
||||
@@ -277,7 +278,7 @@ impl Default for BoxPainting {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
size: vec2(64.0, 32.0),
|
||||
corner_radius: 5.0,
|
||||
rounding: 5.0,
|
||||
stroke_width: 2.0,
|
||||
num_boxes: 1,
|
||||
}
|
||||
@@ -288,7 +289,7 @@ impl BoxPainting {
|
||||
pub fn ui(&mut self, ui: &mut Ui) {
|
||||
ui.add(Slider::new(&mut self.size.x, 0.0..=500.0).text("width"));
|
||||
ui.add(Slider::new(&mut self.size.y, 0.0..=500.0).text("height"));
|
||||
ui.add(Slider::new(&mut self.corner_radius, 0.0..=50.0).text("corner_radius"));
|
||||
ui.add(Slider::new(&mut self.rounding, 0.0..=50.0).text("rounding"));
|
||||
ui.add(Slider::new(&mut self.stroke_width, 0.0..=10.0).text("stroke_width"));
|
||||
ui.add(Slider::new(&mut self.num_boxes, 0..=8).text("num_boxes"));
|
||||
|
||||
@@ -297,7 +298,7 @@ impl BoxPainting {
|
||||
let (rect, _response) = ui.allocate_at_least(self.size, Sense::hover());
|
||||
ui.painter().rect(
|
||||
rect,
|
||||
self.corner_radius,
|
||||
self.rounding,
|
||||
Color32::from_gray(64),
|
||||
Stroke::new(self.stroke_width, Color32::WHITE),
|
||||
);
|
||||
@@ -417,7 +418,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"This is a demonstration of ",
|
||||
first_row_indentation,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: default_color,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -426,7 +426,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"the egui text layout engine. ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: strong_color,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -435,7 +434,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"It supports ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: default_color,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -444,7 +442,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"different ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: Color32::from_rgb(110, 255, 110),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -453,7 +450,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"colors, ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: Color32::from_rgb(128, 140, 255),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -462,7 +458,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"backgrounds, ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: default_color,
|
||||
background: Color32::from_rgb(128, 32, 32),
|
||||
..Default::default()
|
||||
@@ -472,7 +467,7 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"mixing ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Heading,
|
||||
font_id: FontId::proportional(20.0),
|
||||
color: default_color,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -481,7 +476,7 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"fonts, ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Monospace,
|
||||
font_id: FontId::monospace(14.0),
|
||||
color: default_color,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -490,7 +485,7 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"raised text, ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Small,
|
||||
font_id: FontId::proportional(8.0),
|
||||
color: default_color,
|
||||
valign: Align::TOP,
|
||||
..Default::default()
|
||||
@@ -500,7 +495,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"with ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: default_color,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -509,7 +503,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"underlining",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: default_color,
|
||||
underline: Stroke::new(1.0, Color32::LIGHT_BLUE),
|
||||
..Default::default()
|
||||
@@ -519,7 +512,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
" and ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: default_color,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -528,7 +520,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"strikethrough",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: default_color,
|
||||
strikethrough: Stroke::new(2.0, Color32::RED.linear_multiply(0.5)),
|
||||
..Default::default()
|
||||
@@ -538,7 +529,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
". Of course, ",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: default_color,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -547,7 +537,6 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
"you can",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Body,
|
||||
color: default_color,
|
||||
strikethrough: Stroke::new(1.0, strong_color),
|
||||
..Default::default()
|
||||
@@ -557,7 +546,7 @@ fn text_layout_ui(ui: &mut egui::Ui) {
|
||||
" mix these!",
|
||||
0.0,
|
||||
TextFormat {
|
||||
style: TextStyle::Small,
|
||||
font_id: FontId::proportional(8.0),
|
||||
color: Color32::LIGHT_BLUE,
|
||||
background: Color32::from_rgb(128, 0, 0),
|
||||
underline: Stroke::new(1.0, strong_color),
|
||||
|
||||
@@ -16,6 +16,7 @@ pub mod grid_demo;
|
||||
pub mod layout_test;
|
||||
pub mod misc_demo_window;
|
||||
pub mod multi_touch;
|
||||
pub mod paint_bezier;
|
||||
pub mod painting;
|
||||
pub mod password;
|
||||
pub mod plot_demo;
|
||||
@@ -47,5 +48,5 @@ pub trait Demo {
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Show windows, etc
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool);
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ impl super::Demo for MultiTouch {
|
||||
"👌 Multi Touch"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.default_size(vec2(512.0, 512.0))
|
||||
@@ -77,7 +77,7 @@ impl super::View for MultiTouch {
|
||||
// color and width:
|
||||
let mut stroke_width = 1.;
|
||||
let color = Color32::GRAY;
|
||||
if let Some(multi_touch) = ui.input().multi_touch() {
|
||||
if let Some(multi_touch) = ui.ctx().multi_touch() {
|
||||
// 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;
|
||||
|
||||
254
egui_demo_lib/src/apps/demo/paint_bezier.rs
Normal file
254
egui_demo_lib/src/apps/demo/paint_bezier.rs
Normal file
@@ -0,0 +1,254 @@
|
||||
use egui::emath::RectTransform;
|
||||
use egui::epaint::{CircleShape, CubicBezierShape, QuadraticBezierShape};
|
||||
use egui::*;
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct PaintBezier {
|
||||
/// Current bezier curve degree, it can be 3, 4.
|
||||
bezier: usize,
|
||||
/// Track the bezier degree before change in order to clean the remaining points.
|
||||
degree_backup: usize,
|
||||
/// Points already clicked. once it reaches the 'bezier' degree, it will be pushed into the 'shapes'
|
||||
points: Vec<Pos2>,
|
||||
/// Track last points set in order to draw auxiliary lines.
|
||||
backup_points: Vec<Pos2>,
|
||||
/// Quadratic shapes already drawn.
|
||||
q_shapes: Vec<QuadraticBezierShape>,
|
||||
/// Cubic shapes already drawn.
|
||||
/// Since `Shape` can't be 'serialized', we can't use Shape as variable type.
|
||||
c_shapes: Vec<CubicBezierShape>,
|
||||
/// Stroke for auxiliary lines.
|
||||
aux_stroke: Stroke,
|
||||
/// Stroke for bezier curve.
|
||||
stroke: Stroke,
|
||||
/// Fill for bezier curve.
|
||||
fill: Color32,
|
||||
/// The curve should be closed or not.
|
||||
closed: bool,
|
||||
/// Display the bounding box or not.
|
||||
show_bounding_box: bool,
|
||||
/// Storke for the bounding box.
|
||||
bounding_box_stroke: Stroke,
|
||||
}
|
||||
|
||||
impl Default for PaintBezier {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bezier: 4, // default bezier degree, a cubic bezier curve
|
||||
degree_backup: 4,
|
||||
points: Default::default(),
|
||||
backup_points: Default::default(),
|
||||
q_shapes: Default::default(),
|
||||
c_shapes: Default::default(),
|
||||
aux_stroke: Stroke::new(1.0, Color32::RED),
|
||||
stroke: Stroke::new(1.0, Color32::LIGHT_BLUE),
|
||||
fill: Default::default(),
|
||||
closed: false,
|
||||
show_bounding_box: false,
|
||||
bounding_box_stroke: Stroke::new(1.0, Color32::LIGHT_GREEN),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PaintBezier {
|
||||
pub fn ui_control(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
||||
ui.horizontal(|ui| {
|
||||
ui.vertical(|ui| {
|
||||
egui::stroke_ui(ui, &mut self.stroke, "Curve Stroke");
|
||||
egui::stroke_ui(ui, &mut self.aux_stroke, "Auxiliary Stroke");
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Fill Color:");
|
||||
if ui.color_edit_button_srgba(&mut self.fill).changed()
|
||||
&& self.fill != Color32::TRANSPARENT
|
||||
{
|
||||
self.closed = true;
|
||||
}
|
||||
if ui.checkbox(&mut self.closed, "Closed").clicked() && !self.closed {
|
||||
self.fill = Color32::TRANSPARENT;
|
||||
}
|
||||
});
|
||||
egui::stroke_ui(ui, &mut self.bounding_box_stroke, "Bounding Box Stroke");
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
ui.vertical(|ui| {
|
||||
{
|
||||
let mut tessellation_options = *(ui.ctx().tessellation_options());
|
||||
let tessellation_options = &mut tessellation_options;
|
||||
tessellation_options.ui(ui);
|
||||
let mut new_tessellation_options = ui.ctx().tessellation_options();
|
||||
*new_tessellation_options = *tessellation_options;
|
||||
}
|
||||
|
||||
ui.checkbox(&mut self.show_bounding_box, "Bounding Box");
|
||||
});
|
||||
ui.separator();
|
||||
ui.vertical(|ui| {
|
||||
if ui.radio_value(&mut self.bezier, 3, "Quadratic").clicked()
|
||||
&& self.degree_backup != self.bezier
|
||||
{
|
||||
self.points.clear();
|
||||
self.degree_backup = self.bezier;
|
||||
};
|
||||
if ui.radio_value(&mut self.bezier, 4, "Cubic").clicked()
|
||||
&& self.degree_backup != self.bezier
|
||||
{
|
||||
self.points.clear();
|
||||
self.degree_backup = self.bezier;
|
||||
};
|
||||
// ui.radio_value(self.bezier, 5, "Quintic");
|
||||
ui.label("Click 3 or 4 points to build a bezier curve!");
|
||||
if ui.button("Clear Painting").clicked() {
|
||||
self.points.clear();
|
||||
self.backup_points.clear();
|
||||
self.q_shapes.clear();
|
||||
self.c_shapes.clear();
|
||||
}
|
||||
})
|
||||
})
|
||||
.response
|
||||
}
|
||||
|
||||
pub fn ui_content(&mut self, ui: &mut Ui) -> egui::Response {
|
||||
let (mut response, painter) =
|
||||
ui.allocate_painter(ui.available_size_before_wrap(), Sense::click());
|
||||
|
||||
let to_screen = emath::RectTransform::from_to(
|
||||
Rect::from_min_size(Pos2::ZERO, response.rect.square_proportions()),
|
||||
response.rect,
|
||||
);
|
||||
let from_screen = to_screen.inverse();
|
||||
|
||||
if response.clicked() {
|
||||
if let Some(pointer_pos) = response.interact_pointer_pos() {
|
||||
let canvas_pos = from_screen * pointer_pos;
|
||||
self.points.push(canvas_pos);
|
||||
if self.points.len() >= self.bezier {
|
||||
self.backup_points = self.points.clone();
|
||||
let points = self.points.drain(..).collect::<Vec<_>>();
|
||||
match points.len() {
|
||||
3 => {
|
||||
let quadratic = QuadraticBezierShape::from_points_stroke(
|
||||
points,
|
||||
self.closed,
|
||||
self.fill,
|
||||
self.stroke,
|
||||
);
|
||||
self.q_shapes.push(quadratic);
|
||||
}
|
||||
4 => {
|
||||
let cubic = CubicBezierShape::from_points_stroke(
|
||||
points,
|
||||
self.closed,
|
||||
self.fill,
|
||||
self.stroke,
|
||||
);
|
||||
self.c_shapes.push(cubic);
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.mark_changed();
|
||||
}
|
||||
}
|
||||
let mut shapes = Vec::new();
|
||||
for shape in self.q_shapes.iter() {
|
||||
shapes.push(shape.to_screen(&to_screen).into());
|
||||
if self.show_bounding_box {
|
||||
shapes.push(self.build_bounding_box(shape.bounding_rect(), &to_screen));
|
||||
}
|
||||
}
|
||||
for shape in self.c_shapes.iter() {
|
||||
shapes.push(shape.to_screen(&to_screen).into());
|
||||
if self.show_bounding_box {
|
||||
shapes.push(self.build_bounding_box(shape.bounding_rect(), &to_screen));
|
||||
}
|
||||
}
|
||||
painter.extend(shapes);
|
||||
|
||||
if !self.points.is_empty() {
|
||||
painter.extend(build_auxiliary_line(
|
||||
&self.points,
|
||||
&to_screen,
|
||||
&self.aux_stroke,
|
||||
));
|
||||
} else if !self.backup_points.is_empty() {
|
||||
painter.extend(build_auxiliary_line(
|
||||
&self.backup_points,
|
||||
&to_screen,
|
||||
&self.aux_stroke,
|
||||
));
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
|
||||
pub fn build_bounding_box(&self, bbox: Rect, to_screen: &RectTransform) -> Shape {
|
||||
let bbox = Rect {
|
||||
min: to_screen * bbox.min,
|
||||
max: to_screen * bbox.max,
|
||||
};
|
||||
let bbox_shape = epaint::RectShape::stroke(bbox, 0.0, self.bounding_box_stroke);
|
||||
bbox_shape.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// An internal function to create auxiliary lines around the current bezier curve
|
||||
/// or to auxiliary lines (points) before the points meet the bezier curve requirements.
|
||||
fn build_auxiliary_line(
|
||||
points: &[Pos2],
|
||||
to_screen: &RectTransform,
|
||||
aux_stroke: &Stroke,
|
||||
) -> Vec<Shape> {
|
||||
let mut shapes = Vec::new();
|
||||
if points.len() >= 2 {
|
||||
let points: Vec<Pos2> = points.iter().map(|p| to_screen * *p).collect();
|
||||
shapes.push(egui::Shape::line(points, *aux_stroke));
|
||||
}
|
||||
for point in points.iter() {
|
||||
let center = to_screen * *point;
|
||||
let radius = aux_stroke.width * 3.0;
|
||||
let circle = CircleShape {
|
||||
center,
|
||||
radius,
|
||||
fill: aux_stroke.color,
|
||||
stroke: *aux_stroke,
|
||||
};
|
||||
|
||||
shapes.push(circle.into());
|
||||
}
|
||||
|
||||
shapes
|
||||
}
|
||||
|
||||
impl super::Demo for PaintBezier {
|
||||
fn name(&self) -> &'static str {
|
||||
"✔ Bezier Curve"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for PaintBezier {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
// ui.vertical_centered(|ui| {
|
||||
// ui.add(crate::__egui_github_link_file!());
|
||||
// });
|
||||
self.ui_control(ui);
|
||||
|
||||
Frame::dark_canvas(ui.style()).show(ui, |ui| {
|
||||
self.ui_content(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ impl super::Demo for Painting {
|
||||
"🖊 Painting"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
use super::View as _;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
|
||||
@@ -19,7 +19,7 @@ pub fn password_ui(ui: &mut egui::Ui, password: &mut String) -> egui::Response {
|
||||
|
||||
// Get state for this widget.
|
||||
// You should get state by value, not by reference to avoid borrowing of `Memory`.
|
||||
let mut show_plaintext = ui.memory().data.get_temp::<bool>(state_id).unwrap_or(false);
|
||||
let mut show_plaintext = ui.data().get_temp::<bool>(state_id).unwrap_or(false);
|
||||
|
||||
// Process ui, change a local copy of the state
|
||||
// We want TextEdit to fill entire space, and have button after that, so in that case we can
|
||||
@@ -42,7 +42,7 @@ pub fn password_ui(ui: &mut egui::Ui, password: &mut String) -> egui::Response {
|
||||
});
|
||||
|
||||
// Store the (possibly changed) state:
|
||||
ui.memory().data.insert_temp(state_id, show_plaintext);
|
||||
ui.data().insert_temp(state_id, show_plaintext);
|
||||
|
||||
// All done! Return the interaction response so the user can check what happened
|
||||
// (hovered, clicked, …) and maybe show a tooltip:
|
||||
|
||||
@@ -71,8 +71,8 @@ impl LineDemo {
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.style_mut().wrap = Some(false);
|
||||
ui.checkbox(animate, "animate");
|
||||
ui.checkbox(square, "square view")
|
||||
ui.checkbox(animate, "Animate");
|
||||
ui.checkbox(square, "Square view")
|
||||
.on_hover_text("Always keep the viewport square.");
|
||||
ui.checkbox(proportional, "Proportional data axes")
|
||||
.on_hover_text("Tick are the same size on both axes.");
|
||||
@@ -237,19 +237,11 @@ impl Widget for &mut MarkerDemo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(Default, PartialEq)]
|
||||
struct LegendDemo {
|
||||
config: Legend,
|
||||
}
|
||||
|
||||
impl Default for LegendDemo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
config: Legend::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LegendDemo {
|
||||
fn line_with_slope(slope: f64) -> Line {
|
||||
Line::new(Values::from_explicit_callback(move |x| slope * x, .., 100))
|
||||
@@ -269,9 +261,10 @@ impl Widget for &mut LegendDemo {
|
||||
egui::Grid::new("settings").show(ui, |ui| {
|
||||
ui.label("Text style:");
|
||||
ui.horizontal(|ui| {
|
||||
TextStyle::all().for_each(|style| {
|
||||
ui.selectable_value(&mut config.text_style, style, format!("{:?}", style));
|
||||
});
|
||||
let all_text_styles = ui.style().text_styles();
|
||||
for style in all_text_styles {
|
||||
ui.selectable_value(&mut config.text_style, style.clone(), style.to_string());
|
||||
}
|
||||
});
|
||||
ui.end_row();
|
||||
|
||||
@@ -292,7 +285,9 @@ impl Widget for &mut LegendDemo {
|
||||
ui.end_row();
|
||||
});
|
||||
|
||||
let legend_plot = Plot::new("legend_demo").legend(*config).data_aspect(1.0);
|
||||
let legend_plot = Plot::new("legend_demo")
|
||||
.legend(config.clone())
|
||||
.data_aspect(1.0);
|
||||
legend_plot
|
||||
.show(ui, |plot_ui| {
|
||||
plot_ui.line(LegendDemo::line_with_slope(0.5).name("lines"));
|
||||
@@ -305,10 +300,82 @@ impl Widget for &mut LegendDemo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Default)]
|
||||
struct ItemsDemo {}
|
||||
#[derive(PartialEq)]
|
||||
struct LinkedAxisDemo {
|
||||
link_x: bool,
|
||||
link_y: bool,
|
||||
group: plot::LinkedAxisGroup,
|
||||
}
|
||||
|
||||
impl ItemsDemo {}
|
||||
impl Default for LinkedAxisDemo {
|
||||
fn default() -> Self {
|
||||
let link_x = true;
|
||||
let link_y = false;
|
||||
Self {
|
||||
link_x,
|
||||
link_y,
|
||||
group: plot::LinkedAxisGroup::new(link_x, link_y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LinkedAxisDemo {
|
||||
fn line_with_slope(slope: f64) -> Line {
|
||||
Line::new(Values::from_explicit_callback(move |x| slope * x, .., 100))
|
||||
}
|
||||
fn sin() -> Line {
|
||||
Line::new(Values::from_explicit_callback(move |x| x.sin(), .., 100))
|
||||
}
|
||||
fn cos() -> Line {
|
||||
Line::new(Values::from_explicit_callback(move |x| x.cos(), .., 100))
|
||||
}
|
||||
|
||||
fn configure_plot(plot_ui: &mut plot::PlotUi) {
|
||||
plot_ui.line(LinkedAxisDemo::line_with_slope(0.5));
|
||||
plot_ui.line(LinkedAxisDemo::line_with_slope(1.0));
|
||||
plot_ui.line(LinkedAxisDemo::line_with_slope(2.0));
|
||||
plot_ui.line(LinkedAxisDemo::sin());
|
||||
plot_ui.line(LinkedAxisDemo::cos());
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &mut LinkedAxisDemo {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Linked axes:");
|
||||
ui.checkbox(&mut self.link_x, "X");
|
||||
ui.checkbox(&mut self.link_y, "Y");
|
||||
});
|
||||
self.group.set_link_x(self.link_x);
|
||||
self.group.set_link_y(self.link_y);
|
||||
ui.horizontal(|ui| {
|
||||
Plot::new("linked_axis_1")
|
||||
.data_aspect(1.0)
|
||||
.width(250.0)
|
||||
.height(250.0)
|
||||
.link_axis(self.group.clone())
|
||||
.show(ui, LinkedAxisDemo::configure_plot);
|
||||
Plot::new("linked_axis_2")
|
||||
.data_aspect(2.0)
|
||||
.width(150.0)
|
||||
.height(250.0)
|
||||
.link_axis(self.group.clone())
|
||||
.show(ui, LinkedAxisDemo::configure_plot);
|
||||
});
|
||||
Plot::new("linked_axis_3")
|
||||
.data_aspect(0.5)
|
||||
.width(250.0)
|
||||
.height(150.0)
|
||||
.link_axis(self.group.clone())
|
||||
.show(ui, LinkedAxisDemo::configure_plot)
|
||||
.response
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Default)]
|
||||
struct ItemsDemo {
|
||||
texture: Option<egui::TextureHandle>,
|
||||
}
|
||||
|
||||
impl Widget for &mut ItemsDemo {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
@@ -343,13 +410,15 @@ impl Widget for &mut ItemsDemo {
|
||||
);
|
||||
Arrows::new(arrow_origins, arrow_tips)
|
||||
};
|
||||
|
||||
let texture: &egui::TextureHandle = self.texture.get_or_insert_with(|| {
|
||||
ui.ctx()
|
||||
.load_texture("plot_demo", egui::ColorImage::example())
|
||||
});
|
||||
let image = PlotImage::new(
|
||||
TextureId::Egui,
|
||||
texture,
|
||||
Value::new(0.0, 10.0),
|
||||
[
|
||||
ui.fonts().font_image().width as f32 / 100.0,
|
||||
ui.fonts().font_image().height as f32 / 100.0,
|
||||
],
|
||||
5.0 * vec2(texture.aspect_ratio(), 1.0),
|
||||
);
|
||||
|
||||
let plot = Plot::new("items_demo")
|
||||
@@ -376,15 +445,9 @@ impl Widget for &mut ItemsDemo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(Default, PartialEq)]
|
||||
struct InteractionDemo {}
|
||||
|
||||
impl Default for InteractionDemo {
|
||||
fn default() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &mut InteractionDemo {
|
||||
fn ui(self, ui: &mut Ui) -> Response {
|
||||
let plot = Plot::new("interaction_demo").height(300.0);
|
||||
@@ -532,15 +595,40 @@ impl ChartsDemo {
|
||||
.name("Set 4")
|
||||
.stack_on(&[&chart1, &chart2, &chart3]);
|
||||
|
||||
let mut x_fmt: fn(f64) -> String = |val| {
|
||||
if val >= 0.0 && val <= 4.0 && is_approx_integer(val) {
|
||||
// Only label full days from 0 to 4
|
||||
format!("Day {}", val)
|
||||
} else {
|
||||
// Otherwise return empty string (i.e. no label)
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
|
||||
let mut y_fmt: fn(f64) -> String = |val| {
|
||||
let percent = 100.0 * val;
|
||||
|
||||
if is_approx_integer(percent) && !is_approx_zero(percent) {
|
||||
// Only show integer percentages,
|
||||
// and don't show at Y=0 (label overlaps with X axis label)
|
||||
format!("{}%", percent)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
|
||||
if !self.vertical {
|
||||
chart1 = chart1.horizontal();
|
||||
chart2 = chart2.horizontal();
|
||||
chart3 = chart3.horizontal();
|
||||
chart4 = chart4.horizontal();
|
||||
std::mem::swap(&mut x_fmt, &mut y_fmt);
|
||||
}
|
||||
|
||||
Plot::new("Stacked Bar Chart Demo")
|
||||
.legend(Legend::default())
|
||||
.x_axis_formatter(x_fmt)
|
||||
.y_axis_formatter(y_fmt)
|
||||
.data_aspect(1.0)
|
||||
.show(ui, |plot_ui| {
|
||||
plot_ui.bar_chart(chart1);
|
||||
@@ -623,11 +711,12 @@ enum Panel {
|
||||
Charts,
|
||||
Items,
|
||||
Interaction,
|
||||
LinkedAxes,
|
||||
}
|
||||
|
||||
impl Default for Panel {
|
||||
fn default() -> Self {
|
||||
Self::Charts
|
||||
Self::Lines
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,6 +728,7 @@ pub struct PlotDemo {
|
||||
charts_demo: ChartsDemo,
|
||||
items_demo: ItemsDemo,
|
||||
interaction_demo: InteractionDemo,
|
||||
linked_axes_demo: LinkedAxisDemo,
|
||||
open_panel: Panel,
|
||||
}
|
||||
|
||||
@@ -647,7 +737,7 @@ impl super::Demo for PlotDemo {
|
||||
"🗠 Plot"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||
use super::View as _;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
@@ -663,6 +753,7 @@ impl super::View for PlotDemo {
|
||||
egui::reset_button(ui, self);
|
||||
ui.collapsing("Instructions", |ui| {
|
||||
ui.label("Pan by dragging, or scroll (+ shift = horizontal).");
|
||||
ui.label("Box zooming: Right click to zoom in and zoom out using a selection.");
|
||||
if cfg!(target_arch = "wasm32") {
|
||||
ui.label("Zoom with ctrl / ⌘ + pointer wheel, or with pinch gesture.");
|
||||
} else if cfg!(target_os = "macos") {
|
||||
@@ -682,6 +773,7 @@ impl super::View for PlotDemo {
|
||||
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::LinkedAxes, "Linked Axes");
|
||||
});
|
||||
ui.separator();
|
||||
|
||||
@@ -704,6 +796,17 @@ impl super::View for PlotDemo {
|
||||
Panel::Interaction => {
|
||||
ui.add(&mut self.interaction_demo);
|
||||
}
|
||||
Panel::LinkedAxes => {
|
||||
ui.add(&mut self.linked_axes_demo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_approx_zero(val: f64) -> bool {
|
||||
val.abs() < 1e-6
|
||||
}
|
||||
|
||||
fn is_approx_integer(val: f64) -> bool {
|
||||
val.fract().abs() < 1e-6
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ impl super::Demo for Scrolling {
|
||||
"↕ Scrolling"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
@@ -81,7 +81,7 @@ fn huge_content_lines(ui: &mut egui::Ui) {
|
||||
ui.add_space(4.0);
|
||||
|
||||
let text_style = TextStyle::Body;
|
||||
let row_height = ui.fonts()[text_style].row_height();
|
||||
let row_height = ui.text_style_height(&text_style);
|
||||
let num_rows = 10_000;
|
||||
ScrollArea::vertical().auto_shrink([false; 2]).show_rows(
|
||||
ui,
|
||||
@@ -101,8 +101,8 @@ fn huge_content_painter(ui: &mut egui::Ui) {
|
||||
ui.label("A lot of rows, but only the visible ones are painted, so performance is still good:");
|
||||
ui.add_space(4.0);
|
||||
|
||||
let text_style = TextStyle::Body;
|
||||
let row_height = ui.fonts()[text_style].row_height() + ui.spacing().item_spacing.y;
|
||||
let font_id = TextStyle::Body.resolve(ui.style());
|
||||
let row_height = ui.fonts().row_height(&font_id) + ui.spacing().item_spacing.y;
|
||||
let num_rows = 10_000;
|
||||
|
||||
ScrollArea::vertical()
|
||||
@@ -130,7 +130,7 @@ fn huge_content_painter(ui: &mut egui::Ui) {
|
||||
pos2(x, y),
|
||||
Align2::LEFT_TOP,
|
||||
text,
|
||||
text_style,
|
||||
font_id.clone(),
|
||||
ui.visuals().text_color(),
|
||||
);
|
||||
used_rect = used_rect.union(text_rect);
|
||||
@@ -210,32 +210,34 @@ impl super::View for ScrollTo {
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
let (current_scroll, max_scroll) = scroll_area.show(ui, |ui| {
|
||||
if scroll_top {
|
||||
ui.scroll_to_cursor(Align::TOP);
|
||||
}
|
||||
ui.vertical(|ui| {
|
||||
for item in 1..=50 {
|
||||
if track_item && item == self.track_item {
|
||||
let response =
|
||||
ui.colored_label(Color32::YELLOW, format!("This is item {}", item));
|
||||
response.scroll_to_me(self.tack_item_align);
|
||||
} else {
|
||||
ui.label(format!("This is item {}", item));
|
||||
}
|
||||
let (current_scroll, max_scroll) = scroll_area
|
||||
.show(ui, |ui| {
|
||||
if scroll_top {
|
||||
ui.scroll_to_cursor(Align::TOP);
|
||||
}
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
for item in 1..=50 {
|
||||
if track_item && item == self.track_item {
|
||||
let response =
|
||||
ui.colored_label(Color32::YELLOW, format!("This is item {}", item));
|
||||
response.scroll_to_me(self.tack_item_align);
|
||||
} else {
|
||||
ui.label(format!("This is item {}", item));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if scroll_bottom {
|
||||
ui.scroll_to_cursor(Align::BOTTOM);
|
||||
}
|
||||
if scroll_bottom {
|
||||
ui.scroll_to_cursor(Align::BOTTOM);
|
||||
}
|
||||
|
||||
let margin = ui.visuals().clip_rect_margin;
|
||||
let margin = ui.visuals().clip_rect_margin;
|
||||
|
||||
let current_scroll = ui.clip_rect().top() - ui.min_rect().top() + margin;
|
||||
let max_scroll = ui.min_rect().height() - ui.clip_rect().height() + 2.0 * margin;
|
||||
(current_scroll, max_scroll)
|
||||
});
|
||||
let current_scroll = ui.clip_rect().top() - ui.min_rect().top() + margin;
|
||||
let max_scroll = ui.min_rect().height() - ui.clip_rect().height() + 2.0 * margin;
|
||||
(current_scroll, max_scroll)
|
||||
})
|
||||
.inner;
|
||||
ui.separator();
|
||||
|
||||
ui.label(format!(
|
||||
@@ -253,17 +255,11 @@ impl super::View for ScrollTo {
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(PartialEq)]
|
||||
#[derive(Default, PartialEq)]
|
||||
struct ScrollStickTo {
|
||||
n_items: usize,
|
||||
}
|
||||
|
||||
impl Default for ScrollStickTo {
|
||||
fn default() -> Self {
|
||||
Self { n_items: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for ScrollStickTo {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
ui.label("Rows enter from the bottom, we want the scroll handle to start and stay at bottom unless moved");
|
||||
@@ -271,7 +267,7 @@ impl super::View for ScrollStickTo {
|
||||
ui.add_space(4.0);
|
||||
|
||||
let text_style = TextStyle::Body;
|
||||
let row_height = ui.fonts()[text_style].row_height();
|
||||
let row_height = ui.text_style_height(&text_style);
|
||||
ScrollArea::vertical().stick_to_bottom().show_rows(
|
||||
ui,
|
||||
row_height,
|
||||
|
||||
@@ -36,7 +36,7 @@ impl super::Demo for Sliders {
|
||||
"⬌ Sliders"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
|
||||
@@ -13,7 +13,7 @@ impl super::Demo for TableDemo {
|
||||
"☰ Table Demo"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(true)
|
||||
|
||||
@@ -6,7 +6,7 @@ impl super::Demo for CursorTest {
|
||||
"Cursor Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
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);
|
||||
@@ -38,7 +38,7 @@ impl super::Demo for IdTest {
|
||||
"ID Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
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);
|
||||
@@ -52,7 +52,7 @@ impl super::View for IdTest {
|
||||
|
||||
ui.label("\
|
||||
Widgets that store state require unique and persisting identifiers so we can track their state between frames.\n\
|
||||
For instance, collapsable headers needs to store wether or not they are open. \
|
||||
For instance, collapsable headers needs to store whether or not they are open. \
|
||||
Their Id:s are derived from their names. \
|
||||
If you fail to give them unique names then clicking one will open both. \
|
||||
To help you debug this, an error message is printed on screen:");
|
||||
@@ -115,7 +115,7 @@ impl super::Demo for ManualLayoutTest {
|
||||
"Manual Layout Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.resizable(false)
|
||||
.open(open)
|
||||
@@ -202,7 +202,7 @@ impl super::Demo for TableTest {
|
||||
"Table Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
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);
|
||||
@@ -314,7 +314,7 @@ impl super::Demo for InputTest {
|
||||
"Input Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
@@ -383,7 +383,7 @@ impl super::Demo for WindowResizeTest {
|
||||
"↔ Window Resize"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
use egui::*;
|
||||
|
||||
Window::new("↔ auto-sized")
|
||||
|
||||
@@ -19,7 +19,7 @@ impl super::Demo for TextEdit {
|
||||
"🖹 TextEdit"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
|
||||
@@ -23,6 +23,8 @@ pub struct WidgetGallery {
|
||||
#[cfg(feature = "datetime")]
|
||||
#[serde(with = "serde_date_format")]
|
||||
date: chrono::Date<chrono::Utc>,
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
texture: Option<egui::TextureHandle>,
|
||||
}
|
||||
|
||||
impl Default for WidgetGallery {
|
||||
@@ -38,6 +40,7 @@ impl Default for WidgetGallery {
|
||||
animate_progress_bar: false,
|
||||
#[cfg(feature = "datetime")]
|
||||
date: chrono::offset::Utc::now().date(),
|
||||
texture: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +50,7 @@ impl super::Demo for WidgetGallery {
|
||||
"🗄 Widget Gallery"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(true)
|
||||
@@ -108,8 +111,14 @@ impl WidgetGallery {
|
||||
color,
|
||||
animate_progress_bar,
|
||||
date,
|
||||
texture,
|
||||
} = self;
|
||||
|
||||
let texture: &egui::TextureHandle = texture.get_or_insert_with(|| {
|
||||
ui.ctx()
|
||||
.load_texture("example", egui::ColorImage::example())
|
||||
});
|
||||
|
||||
ui.add(doc_link_label("Label", "label,heading"));
|
||||
ui.label("Welcome to the widget gallery!");
|
||||
ui.end_row();
|
||||
@@ -189,17 +198,14 @@ impl WidgetGallery {
|
||||
ui.color_edit_button_srgba(color);
|
||||
ui.end_row();
|
||||
|
||||
let img_size = 16.0 * texture.size_vec2() / texture.size_vec2().y;
|
||||
|
||||
ui.add(doc_link_label("Image", "Image"));
|
||||
ui.image(egui::TextureId::Egui, [24.0, 16.0])
|
||||
.on_hover_text("The egui font texture was the convenient choice to show here.");
|
||||
ui.image(texture, img_size);
|
||||
ui.end_row();
|
||||
|
||||
ui.add(doc_link_label("ImageButton", "ImageButton"));
|
||||
if ui
|
||||
.add(egui::ImageButton::new(egui::TextureId::Egui, [24.0, 16.0]))
|
||||
.on_hover_text("The egui font texture was the convenient choice to show here.")
|
||||
.clicked()
|
||||
{
|
||||
if ui.add(egui::ImageButton::new(texture, img_size)).clicked() {
|
||||
*boolean = !*boolean;
|
||||
}
|
||||
ui.end_row();
|
||||
@@ -218,10 +224,11 @@ impl WidgetGallery {
|
||||
ui.add(doc_link_label("CollapsingHeader", "collapsing"));
|
||||
ui.collapsing("Click to see what is hidden!", |ui| {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.label(
|
||||
"Not much, as it turns out - but here is a gold star for you for checking:",
|
||||
);
|
||||
ui.colored_label(egui::Color32::GOLD, "☆");
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
ui.label("It's a ");
|
||||
ui.add(doc_link_label("Spinner", "spinner"));
|
||||
ui.add_space(4.0);
|
||||
ui.add(egui::Spinner::new());
|
||||
});
|
||||
});
|
||||
ui.end_row();
|
||||
@@ -239,10 +246,6 @@ impl WidgetGallery {
|
||||
This toggle switch is just 15 lines of code.",
|
||||
);
|
||||
ui.end_row();
|
||||
|
||||
ui.add(doc_link_label("Spinner", "spinner"));
|
||||
ui.add(egui::Spinner::new());
|
||||
ui.end_row();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ impl super::Demo for WindowOptions {
|
||||
"🗖 Window Options"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
let Self {
|
||||
title,
|
||||
title_bar,
|
||||
|
||||
@@ -7,7 +7,7 @@ impl super::Demo for WindowWithPanels {
|
||||
"🗖 Window With Panels"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
use super::View as _;
|
||||
let window = egui::Window::new("Window with Panels")
|
||||
.default_width(600.0)
|
||||
|
||||
@@ -37,7 +37,7 @@ impl epi::App for FractalClock {
|
||||
"🕑 Fractal Clock"
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::CtxRef, _frame: &epi::Frame) {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
|
||||
egui::CentralPanel::default()
|
||||
.frame(Frame::dark_canvas(&ctx.style()))
|
||||
.show(ctx, |ui| self.ui(ui, crate::seconds_since_midnight()));
|
||||
@@ -97,6 +97,7 @@ impl FractalClock {
|
||||
"Inspired by a screensaver by Rob Mayoff",
|
||||
"http://www.dqd.com/~mayoff/programs/FractalClock/",
|
||||
);
|
||||
ui.add(crate::__egui_github_link_file!());
|
||||
}
|
||||
|
||||
fn paint(&mut self, painter: &Painter) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::sync::mpsc::Receiver;
|
||||
use poll_promise::Promise;
|
||||
|
||||
struct Resource {
|
||||
/// HTTP response
|
||||
@@ -7,7 +7,7 @@ struct Resource {
|
||||
text: Option<String>,
|
||||
|
||||
/// If set, the response was an image.
|
||||
image: Option<epi::Image>,
|
||||
texture: Option<egui::TextureHandle>,
|
||||
|
||||
/// If set, the response was text with some supported syntax highlighting (e.g. ".rs" or ".md").
|
||||
colored_text: Option<ColoredText>,
|
||||
@@ -17,21 +17,21 @@ impl Resource {
|
||||
fn from_response(ctx: &egui::Context, response: ehttp::Response) -> Self {
|
||||
let content_type = response.content_type().unwrap_or_default();
|
||||
let image = if content_type.starts_with("image/") {
|
||||
decode_image(&response.bytes)
|
||||
load_image(&response.bytes).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let text = response.text();
|
||||
let texture = image.map(|image| ctx.load_texture(&response.url, image));
|
||||
|
||||
let colored_text = text
|
||||
.as_ref()
|
||||
.and_then(|text| syntax_highlighting(ctx, &response, text));
|
||||
let text = response.text();
|
||||
let colored_text = text.and_then(|text| syntax_highlighting(ctx, &response, text));
|
||||
let text = text.map(|text| text.to_owned());
|
||||
|
||||
Self {
|
||||
response,
|
||||
text,
|
||||
image,
|
||||
texture,
|
||||
colored_text,
|
||||
}
|
||||
}
|
||||
@@ -42,22 +42,14 @@ pub struct HttpApp {
|
||||
url: String,
|
||||
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
in_progress: Option<Receiver<Result<ehttp::Response, String>>>,
|
||||
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
result: Option<Result<Resource, String>>,
|
||||
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
tex_mngr: TexMngr,
|
||||
promise: Option<Promise<ehttp::Result<Resource>>>,
|
||||
}
|
||||
|
||||
impl Default for HttpApp {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
url: "https://raw.githubusercontent.com/emilk/egui/master/README.md".to_owned(),
|
||||
in_progress: Default::default(),
|
||||
result: Default::default(),
|
||||
tex_mngr: Default::default(),
|
||||
promise: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,15 +59,7 @@ impl epi::App for HttpApp {
|
||||
"⬇ HTTP"
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
|
||||
if let Some(receiver) = &mut self.in_progress {
|
||||
// Are we there yet?
|
||||
if let Ok(result) = receiver.try_recv() {
|
||||
self.in_progress = None;
|
||||
self.result = Some(result.map(|response| Resource::from_response(ctx, response)));
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &epi::Frame) {
|
||||
egui::TopBottomPanel::bottom("http_bottom").show(ctx, |ui| {
|
||||
let layout = egui::Layout::top_down(egui::Align::Center).with_main_justify(true);
|
||||
ui.allocate_ui_with_layout(ui.available_size(), layout, |ui| {
|
||||
@@ -94,33 +78,36 @@ impl epi::App for HttpApp {
|
||||
});
|
||||
|
||||
if trigger_fetch {
|
||||
let request = ehttp::Request::get(&self.url);
|
||||
let ctx = ctx.clone();
|
||||
let frame = frame.clone();
|
||||
let (sender, receiver) = std::sync::mpsc::channel();
|
||||
self.in_progress = Some(receiver);
|
||||
|
||||
let (sender, promise) = Promise::new();
|
||||
let request = ehttp::Request::get(&self.url);
|
||||
ehttp::fetch(request, move |response| {
|
||||
sender.send(response).ok();
|
||||
frame.request_repaint();
|
||||
frame.request_repaint(); // wake up UI thread
|
||||
let resource = response.map(|response| Resource::from_response(&ctx, response));
|
||||
sender.send(resource);
|
||||
});
|
||||
self.promise = Some(promise);
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
if self.in_progress.is_some() {
|
||||
ui.label("Please wait…");
|
||||
} else if let Some(result) = &self.result {
|
||||
match result {
|
||||
Ok(resource) => {
|
||||
ui_resource(ui, frame, &mut self.tex_mngr, resource);
|
||||
}
|
||||
Err(error) => {
|
||||
// This should only happen if the fetch API isn't available or something similar.
|
||||
ui.colored_label(
|
||||
egui::Color32::RED,
|
||||
if error.is_empty() { "Error" } else { error },
|
||||
);
|
||||
if let Some(promise) = &self.promise {
|
||||
if let Some(result) = promise.ready() {
|
||||
match result {
|
||||
Ok(resource) => {
|
||||
ui_resource(ui, resource);
|
||||
}
|
||||
Err(error) => {
|
||||
// This should only happen if the fetch API isn't available or something similar.
|
||||
ui.colored_label(
|
||||
egui::Color32::RED,
|
||||
if error.is_empty() { "Error" } else { error },
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ui.add(egui::Spinner::new());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -160,11 +147,11 @@ fn ui_url(ui: &mut egui::Ui, frame: &epi::Frame, url: &mut String) -> bool {
|
||||
trigger_fetch
|
||||
}
|
||||
|
||||
fn ui_resource(ui: &mut egui::Ui, frame: &epi::Frame, tex_mngr: &mut TexMngr, resource: &Resource) {
|
||||
fn ui_resource(ui: &mut egui::Ui, resource: &Resource) {
|
||||
let Resource {
|
||||
response,
|
||||
text,
|
||||
image,
|
||||
texture,
|
||||
colored_text,
|
||||
} = resource;
|
||||
|
||||
@@ -211,12 +198,10 @@ fn ui_resource(ui: &mut egui::Ui, frame: &epi::Frame, tex_mngr: &mut TexMngr, re
|
||||
ui.separator();
|
||||
}
|
||||
|
||||
if let Some(image) = image {
|
||||
if let Some(texture_id) = tex_mngr.texture(frame, &response.url, image) {
|
||||
let mut size = egui::Vec2::new(image.size[0] as f32, image.size[1] as f32);
|
||||
size *= (ui.available_width() / size.x).min(1.0);
|
||||
ui.image(texture_id, size);
|
||||
}
|
||||
if let Some(texture) = texture {
|
||||
let mut size = texture.size_vec2();
|
||||
size *= (ui.available_width() / size.x).min(1.0);
|
||||
ui.image(texture, size);
|
||||
} else if let Some(colored_text) = colored_text {
|
||||
colored_text.ui(ui);
|
||||
} else if let Some(text) = &text {
|
||||
@@ -231,7 +216,7 @@ fn selectable_text(ui: &mut egui::Ui, mut text: &str) {
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut text)
|
||||
.desired_width(f32::INFINITY)
|
||||
.text_style(egui::TextStyle::Monospace),
|
||||
.font(egui::TextStyle::Monospace),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -272,7 +257,7 @@ impl ColoredText {
|
||||
let mut text = self.0.text.as_str();
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut text)
|
||||
.text_style(egui::TextStyle::Monospace)
|
||||
.font(egui::TextStyle::Monospace)
|
||||
.desired_width(f32::INFINITY)
|
||||
.layouter(&mut layouter),
|
||||
);
|
||||
@@ -287,39 +272,14 @@ impl ColoredText {
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Texture/image handling is very manual at the moment.
|
||||
|
||||
/// Immediate mode texture manager that supports at most one texture at the time :)
|
||||
#[derive(Default)]
|
||||
struct TexMngr {
|
||||
loaded_url: String,
|
||||
texture_id: Option<egui::TextureId>,
|
||||
}
|
||||
|
||||
impl TexMngr {
|
||||
fn texture(
|
||||
&mut self,
|
||||
frame: &epi::Frame,
|
||||
url: &str,
|
||||
image: &epi::Image,
|
||||
) -> Option<egui::TextureId> {
|
||||
if self.loaded_url != url {
|
||||
if let Some(texture_id) = self.texture_id.take() {
|
||||
frame.free_texture(texture_id);
|
||||
}
|
||||
|
||||
self.texture_id = Some(frame.alloc_texture(image.clone()));
|
||||
self.loaded_url = url.to_owned();
|
||||
}
|
||||
self.texture_id
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_image(bytes: &[u8]) -> Option<epi::Image> {
|
||||
use image::GenericImageView;
|
||||
let image = image::load_from_memory(bytes).ok()?;
|
||||
fn load_image(image_data: &[u8]) -> Result<egui::ColorImage, image::ImageError> {
|
||||
let image = image::load_from_memory(image_data)?;
|
||||
let size = [image.width() as _, image.height() as _];
|
||||
let image_buffer = image.to_rgba8();
|
||||
let size = [image.width() as usize, image.height() as usize];
|
||||
let pixels = image_buffer.into_vec();
|
||||
Some(epi::Image::from_rgba_unmultiplied(size, &pixels))
|
||||
let pixels = image_buffer.as_flat_samples();
|
||||
Ok(egui::ColorImage::from_rgba_unmultiplied(
|
||||
size,
|
||||
pixels.as_slice(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ pub struct BackendPanel {
|
||||
run_mode: RunMode,
|
||||
|
||||
/// current slider value for current gui scale
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
pixels_per_point: Option<f32>,
|
||||
|
||||
/// maximum size of the web browser canvas
|
||||
@@ -78,7 +79,7 @@ impl Default for BackendPanel {
|
||||
}
|
||||
|
||||
impl BackendPanel {
|
||||
pub fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
|
||||
pub fn update(&mut self, ctx: &egui::Context, frame: &epi::Frame) {
|
||||
self.frame_history
|
||||
.on_new_frame(ctx.input().time, frame.info().cpu_usage);
|
||||
|
||||
@@ -88,7 +89,7 @@ impl BackendPanel {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_of_frame(&mut self, ctx: &egui::CtxRef) {
|
||||
pub fn end_of_frame(&mut self, ctx: &egui::Context) {
|
||||
self.egui_windows.windows(ctx);
|
||||
}
|
||||
|
||||
@@ -127,9 +128,9 @@ impl BackendPanel {
|
||||
ui.separator();
|
||||
|
||||
{
|
||||
let mut screen_reader = ui.ctx().memory().options.screen_reader;
|
||||
let mut screen_reader = ui.ctx().options().screen_reader;
|
||||
ui.checkbox(&mut screen_reader, "🔈 Screen reader").on_hover_text("Experimental feature: checking this will turn on the screen reader on supported platforms");
|
||||
ui.ctx().memory().options.screen_reader = screen_reader;
|
||||
ui.ctx().options().screen_reader = screen_reader;
|
||||
}
|
||||
|
||||
if !frame.is_web() {
|
||||
@@ -195,12 +196,10 @@ impl BackendPanel {
|
||||
ui: &mut egui::Ui,
|
||||
info: &epi::IntegrationInfo,
|
||||
) -> Option<f32> {
|
||||
self.pixels_per_point = self
|
||||
.pixels_per_point
|
||||
.or(info.native_pixels_per_point)
|
||||
.or_else(|| Some(ui.ctx().pixels_per_point()));
|
||||
|
||||
let pixels_per_point = self.pixels_per_point.as_mut()?;
|
||||
let pixels_per_point = self.pixels_per_point.get_or_insert_with(|| {
|
||||
info.native_pixels_per_point
|
||||
.unwrap_or_else(|| ui.ctx().pixels_per_point())
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().slider_width = 90.0;
|
||||
@@ -325,7 +324,7 @@ impl EguiWindows {
|
||||
ui.checkbox(output_events, "📤 Output Events");
|
||||
}
|
||||
|
||||
fn windows(&mut self, ctx: &egui::CtxRef) {
|
||||
fn windows(&mut self, ctx: &egui::Context) {
|
||||
let Self {
|
||||
settings,
|
||||
inspection,
|
||||
|
||||
@@ -34,7 +34,7 @@ impl epi::App for EasyMarkEditor {
|
||||
"🖹 EasyMark editor"
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::CtxRef, _frame: &epi::Frame) {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
|
||||
egui::TopBottomPanel::bottom("easy_mark_bottom").show(ctx, |ui| {
|
||||
let layout = egui::Layout::top_down(egui::Align::Center).with_main_justify(true);
|
||||
ui.allocate_ui_with_layout(ui.available_size(), layout, |ui| {
|
||||
@@ -88,7 +88,7 @@ impl EasyMarkEditor {
|
||||
|
||||
let response = if self.highlight_editor {
|
||||
let mut layouter = |ui: &egui::Ui, easymark: &str, wrap_width: f32| {
|
||||
let mut layout_job = highlighter.highlight(ui.visuals(), easymark);
|
||||
let mut layout_job = highlighter.highlight(ui.style(), easymark);
|
||||
layout_job.wrap_width = wrap_width;
|
||||
ui.fonts().layout_job(layout_job)
|
||||
};
|
||||
@@ -96,7 +96,7 @@ impl EasyMarkEditor {
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(code)
|
||||
.desired_width(f32::INFINITY)
|
||||
.text_style(egui::TextStyle::Monospace) // for cursor height
|
||||
.font(egui::TextStyle::Monospace) // for cursor height
|
||||
.layouter(&mut layouter),
|
||||
)
|
||||
} else {
|
||||
|
||||
@@ -5,23 +5,23 @@ use crate::easy_mark::easy_mark_parser;
|
||||
/// In practice, the highlighter is fast enough not to need any caching.
|
||||
#[derive(Default)]
|
||||
pub struct MemoizedEasymarkHighlighter {
|
||||
visuals: egui::Visuals,
|
||||
style: egui::Style,
|
||||
code: String,
|
||||
output: egui::text::LayoutJob,
|
||||
}
|
||||
|
||||
impl MemoizedEasymarkHighlighter {
|
||||
pub fn highlight(&mut self, visuals: &egui::Visuals, code: &str) -> egui::text::LayoutJob {
|
||||
if (&self.visuals, self.code.as_str()) != (visuals, code) {
|
||||
self.visuals = visuals.clone();
|
||||
pub fn highlight(&mut self, egui_style: &egui::Style, code: &str) -> egui::text::LayoutJob {
|
||||
if (&self.style, self.code.as_str()) != (egui_style, code) {
|
||||
self.style = egui_style.clone();
|
||||
self.code = code.to_owned();
|
||||
self.output = highlight_easymark(visuals, code);
|
||||
self.output = highlight_easymark(egui_style, code);
|
||||
}
|
||||
self.output.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlight_easymark(visuals: &egui::Visuals, mut text: &str) -> egui::text::LayoutJob {
|
||||
pub fn highlight_easymark(egui_style: &egui::Style, mut text: &str) -> egui::text::LayoutJob {
|
||||
let mut job = egui::text::LayoutJob::default();
|
||||
let mut style = easy_mark_parser::Style::default();
|
||||
let mut start_of_line = true;
|
||||
@@ -33,7 +33,7 @@ pub fn highlight_easymark(visuals: &egui::Visuals, mut text: &str) -> egui::text
|
||||
&text[..end],
|
||||
0.0,
|
||||
format_from_style(
|
||||
visuals,
|
||||
egui_style,
|
||||
&easy_mark_parser::Style {
|
||||
code: true,
|
||||
..Default::default()
|
||||
@@ -50,7 +50,7 @@ pub fn highlight_easymark(visuals: &egui::Visuals, mut text: &str) -> egui::text
|
||||
let end = text[1..]
|
||||
.find(&['`', '\n'][..])
|
||||
.map_or_else(|| text.len(), |i| i + 2);
|
||||
job.append(&text[..end], 0.0, format_from_style(visuals, &style));
|
||||
job.append(&text[..end], 0.0, format_from_style(egui_style, &style));
|
||||
text = &text[end..];
|
||||
style.code = false;
|
||||
continue;
|
||||
@@ -61,7 +61,7 @@ pub fn highlight_easymark(visuals: &egui::Visuals, mut text: &str) -> egui::text
|
||||
if text.starts_with('\\') && text.len() >= 2 {
|
||||
skip = 2;
|
||||
} else if start_of_line && text.starts_with(' ') {
|
||||
// indentation we don't preview indentation, because it is confusing
|
||||
// we don't preview indentation, because it is confusing
|
||||
skip = 1;
|
||||
} else if start_of_line && text.starts_with("# ") {
|
||||
style.heading = true;
|
||||
@@ -69,15 +69,15 @@ pub fn highlight_easymark(visuals: &egui::Visuals, mut text: &str) -> egui::text
|
||||
} else if start_of_line && text.starts_with("> ") {
|
||||
style.quoted = true;
|
||||
skip = 2;
|
||||
// indentation we don't preview indentation, because it is confusing
|
||||
// we don't preview indentation, because it is confusing
|
||||
} else if start_of_line && text.starts_with("- ") {
|
||||
skip = 2;
|
||||
// indentation we don't preview indentation, because it is confusing
|
||||
// we don't preview indentation, because it is confusing
|
||||
} else if text.starts_with('*') {
|
||||
skip = 1;
|
||||
if style.strong {
|
||||
// Include the character that i ending ths style:
|
||||
job.append(&text[..skip], 0.0, format_from_style(visuals, &style));
|
||||
// Include the character that is ending this style:
|
||||
job.append(&text[..skip], 0.0, format_from_style(egui_style, &style));
|
||||
text = &text[skip..];
|
||||
skip = 0;
|
||||
}
|
||||
@@ -85,8 +85,8 @@ pub fn highlight_easymark(visuals: &egui::Visuals, mut text: &str) -> egui::text
|
||||
} else if text.starts_with('$') {
|
||||
skip = 1;
|
||||
if style.small {
|
||||
// Include the character that i ending ths style:
|
||||
job.append(&text[..skip], 0.0, format_from_style(visuals, &style));
|
||||
// Include the character that is ending this style:
|
||||
job.append(&text[..skip], 0.0, format_from_style(egui_style, &style));
|
||||
text = &text[skip..];
|
||||
skip = 0;
|
||||
}
|
||||
@@ -94,8 +94,8 @@ pub fn highlight_easymark(visuals: &egui::Visuals, mut text: &str) -> egui::text
|
||||
} else if text.starts_with('^') {
|
||||
skip = 1;
|
||||
if style.raised {
|
||||
// Include the character that i ending ths style:
|
||||
job.append(&text[..skip], 0.0, format_from_style(visuals, &style));
|
||||
// Include the character that is ending this style:
|
||||
job.append(&text[..skip], 0.0, format_from_style(egui_style, &style));
|
||||
text = &text[skip..];
|
||||
skip = 0;
|
||||
}
|
||||
@@ -114,12 +114,16 @@ pub fn highlight_easymark(visuals: &egui::Visuals, mut text: &str) -> egui::text
|
||||
.map_or_else(|| text.len(), |i| (skip + i).max(1));
|
||||
|
||||
if line_end <= end {
|
||||
job.append(&text[..line_end], 0.0, format_from_style(visuals, &style));
|
||||
job.append(
|
||||
&text[..line_end],
|
||||
0.0,
|
||||
format_from_style(egui_style, &style),
|
||||
);
|
||||
text = &text[line_end..];
|
||||
start_of_line = true;
|
||||
style = Default::default();
|
||||
} else {
|
||||
job.append(&text[..end], 0.0, format_from_style(visuals, &style));
|
||||
job.append(&text[..end], 0.0, format_from_style(egui_style, &style));
|
||||
text = &text[end..];
|
||||
start_of_line = false;
|
||||
}
|
||||
@@ -129,17 +133,17 @@ pub fn highlight_easymark(visuals: &egui::Visuals, mut text: &str) -> egui::text
|
||||
}
|
||||
|
||||
fn format_from_style(
|
||||
visuals: &egui::Visuals,
|
||||
egui_style: &egui::Style,
|
||||
emark_style: &easy_mark_parser::Style,
|
||||
) -> egui::text::TextFormat {
|
||||
use egui::{Align, Color32, Stroke, TextStyle};
|
||||
|
||||
let color = if emark_style.strong || emark_style.heading {
|
||||
visuals.strong_text_color()
|
||||
egui_style.visuals.strong_text_color()
|
||||
} else if emark_style.quoted {
|
||||
visuals.weak_text_color()
|
||||
egui_style.visuals.weak_text_color()
|
||||
} else {
|
||||
visuals.text_color()
|
||||
egui_style.visuals.text_color()
|
||||
};
|
||||
|
||||
let text_style = if emark_style.heading {
|
||||
@@ -153,7 +157,7 @@ fn format_from_style(
|
||||
};
|
||||
|
||||
let background = if emark_style.code {
|
||||
visuals.code_bg_color
|
||||
egui_style.visuals.code_bg_color
|
||||
} else {
|
||||
Color32::TRANSPARENT
|
||||
};
|
||||
@@ -177,7 +181,7 @@ fn format_from_style(
|
||||
};
|
||||
|
||||
egui::text::TextFormat {
|
||||
style: text_style,
|
||||
font_id: text_style.resolve(egui_style),
|
||||
color,
|
||||
background,
|
||||
italics: emark_style.italics,
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
//! A parser for `EasyMark`: a very simple markup language.
|
||||
//!
|
||||
//! WARNING: `EasyMark` is subject to change.
|
||||
//!
|
||||
//! This module does not depend on anything else in egui
|
||||
//! and should perhaps be its own crate.
|
||||
//
|
||||
//! # `EasyMark` design goals:
|
||||
//! 1. easy to parse
|
||||
|
||||
@@ -18,7 +18,8 @@ pub fn easy_mark_it<'em>(ui: &mut Ui, items: impl Iterator<Item = easy_mark::Ite
|
||||
|
||||
ui.allocate_ui_with_layout(initial_size, layout, |ui| {
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
ui.set_row_height(ui.fonts()[TextStyle::Body].row_height());
|
||||
let row_height = ui.text_style_height(&TextStyle::Body);
|
||||
ui.set_row_height(row_height);
|
||||
|
||||
for item in items {
|
||||
item_ui(ui, item);
|
||||
@@ -27,7 +28,7 @@ pub fn easy_mark_it<'em>(ui: &mut Ui, items: impl Iterator<Item = easy_mark::Ite
|
||||
}
|
||||
|
||||
pub fn item_ui(ui: &mut Ui, item: easy_mark::Item<'_>) {
|
||||
let row_height = ui.fonts()[TextStyle::Body].row_height();
|
||||
let row_height = ui.text_style_height(&TextStyle::Body);
|
||||
let one_indent = row_height / 2.0;
|
||||
|
||||
match item {
|
||||
@@ -133,7 +134,7 @@ fn rich_text_from_style(text: &str, style: &easy_mark::Style) -> RichText {
|
||||
}
|
||||
|
||||
fn bullet_point(ui: &mut Ui, width: f32) -> Response {
|
||||
let row_height = ui.fonts()[TextStyle::Body].row_height();
|
||||
let row_height = ui.text_style_height(&TextStyle::Body);
|
||||
let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover());
|
||||
ui.painter().circle_filled(
|
||||
rect.center(),
|
||||
@@ -144,7 +145,8 @@ fn bullet_point(ui: &mut Ui, width: f32) -> Response {
|
||||
}
|
||||
|
||||
fn numbered_point(ui: &mut Ui, width: f32, number: &str) -> Response {
|
||||
let row_height = ui.fonts()[TextStyle::Body].row_height();
|
||||
let font_id = TextStyle::Body.resolve(ui.style());
|
||||
let row_height = ui.fonts().row_height(&font_id);
|
||||
let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover());
|
||||
let text = format!("{}.", number);
|
||||
let text_color = ui.visuals().strong_text_color();
|
||||
@@ -152,7 +154,7 @@ fn numbered_point(ui: &mut Ui, width: f32, number: &str) -> Response {
|
||||
rect.right_center(),
|
||||
Align2::RIGHT_CENTER,
|
||||
text,
|
||||
TextStyle::Body,
|
||||
font_id,
|
||||
text_color,
|
||||
);
|
||||
response
|
||||
|
||||
@@ -76,7 +76,7 @@ impl FrameHistory {
|
||||
let mut shapes = Vec::with_capacity(3 + 2 * history.len());
|
||||
shapes.push(Shape::Rect(epaint::RectShape {
|
||||
rect,
|
||||
corner_radius: style.corner_radius,
|
||||
rounding: style.rounding,
|
||||
fill: ui.visuals().extreme_bg_color,
|
||||
stroke: ui.style().noninteractive().bg_stroke,
|
||||
}));
|
||||
@@ -94,11 +94,11 @@ impl FrameHistory {
|
||||
let cpu_usage = to_screen.inverse().transform_pos(pointer_pos).y;
|
||||
let text = format!("{:.1} ms", 1e3 * cpu_usage);
|
||||
shapes.push(Shape::text(
|
||||
ui.fonts(),
|
||||
&*ui.fonts(),
|
||||
pos2(rect.left(), y),
|
||||
egui::Align2::LEFT_BOTTOM,
|
||||
text,
|
||||
TextStyle::Monospace,
|
||||
TextStyle::Monospace.resolve(ui.style()),
|
||||
color,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, tur
|
||||
#[test]
|
||||
fn test_egui_e2e() {
|
||||
let mut demo_windows = crate::DemoWindows::default();
|
||||
let mut ctx = egui::CtxRef::default();
|
||||
let ctx = egui::Context::default();
|
||||
let raw_input = egui::RawInput::default();
|
||||
|
||||
const NUM_FRAMES: usize = 5;
|
||||
@@ -156,7 +156,7 @@ fn test_egui_e2e() {
|
||||
#[test]
|
||||
fn test_egui_zero_window_size() {
|
||||
let mut demo_windows = crate::DemoWindows::default();
|
||||
let mut ctx = egui::CtxRef::default();
|
||||
let ctx = egui::Context::default();
|
||||
let raw_input = egui::RawInput {
|
||||
screen_rect: Some(egui::Rect::from_min_max(egui::Pos2::ZERO, egui::Pos2::ZERO)),
|
||||
..Default::default()
|
||||
@@ -176,7 +176,7 @@ fn test_egui_zero_window_size() {
|
||||
|
||||
/// Time of day as seconds since midnight. Used for clock in demo app.
|
||||
pub(crate) fn seconds_since_midnight() -> Option<f64> {
|
||||
#[cfg(feature = "chrono")]
|
||||
#[cfg(feature = "datetime")]
|
||||
{
|
||||
use chrono::Timelike;
|
||||
let time = chrono::Local::now().time();
|
||||
@@ -184,6 +184,6 @@ pub(crate) fn seconds_since_midnight() -> Option<f64> {
|
||||
time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64);
|
||||
Some(seconds_since_midnight)
|
||||
}
|
||||
#[cfg(not(feature = "chrono"))]
|
||||
#[cfg(not(feature = "datetime"))]
|
||||
None
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn code_view_ui(ui: &mut egui::Ui, mut code: &str) {
|
||||
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut code)
|
||||
.text_style(egui::TextStyle::Monospace) // for cursor height
|
||||
.font(egui::TextStyle::Monospace) // for cursor height
|
||||
.code_editor()
|
||||
.desired_rows(1)
|
||||
.lock_focus(true)
|
||||
@@ -66,7 +66,7 @@ enum SyntectTheme {
|
||||
|
||||
#[cfg(feature = "syntect")]
|
||||
impl SyntectTheme {
|
||||
fn all() -> impl Iterator<Item = Self> {
|
||||
fn all() -> impl ExactSizeIterator<Item = Self> {
|
||||
[
|
||||
Self::Base16EightiesDark,
|
||||
Self::Base16MochaDark,
|
||||
@@ -116,7 +116,7 @@ impl SyntectTheme {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Hash, PartialEq)]
|
||||
#[derive(Clone, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct CodeTheme {
|
||||
@@ -146,27 +146,21 @@ impl CodeTheme {
|
||||
|
||||
pub fn from_memory(ctx: &egui::Context) -> Self {
|
||||
if ctx.style().visuals.dark_mode {
|
||||
ctx.memory()
|
||||
.data
|
||||
ctx.data()
|
||||
.get_persisted(egui::Id::new("dark"))
|
||||
.unwrap_or_else(CodeTheme::dark)
|
||||
} else {
|
||||
ctx.memory()
|
||||
.data
|
||||
ctx.data()
|
||||
.get_persisted(egui::Id::new("light"))
|
||||
.unwrap_or_else(CodeTheme::light)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn store_in_memory(&self, ctx: &egui::Context) {
|
||||
pub fn store_in_memory(self, ctx: &egui::Context) {
|
||||
if self.dark_mode {
|
||||
ctx.memory()
|
||||
.data
|
||||
.insert_persisted(egui::Id::new("dark"), *self);
|
||||
ctx.data().insert_persisted(egui::Id::new("dark"), self);
|
||||
} else {
|
||||
ctx.memory()
|
||||
.data
|
||||
.insert_persisted(egui::Id::new("light"), *self);
|
||||
ctx.data().insert_persisted(egui::Id::new("light"), self);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,34 +195,34 @@ impl CodeTheme {
|
||||
#[cfg(not(feature = "syntect"))]
|
||||
impl CodeTheme {
|
||||
pub fn dark() -> Self {
|
||||
let text_style = egui::TextStyle::Monospace;
|
||||
let font_id = egui::FontId::monospace(12.0);
|
||||
use egui::{Color32, TextFormat};
|
||||
Self {
|
||||
dark_mode: true,
|
||||
formats: enum_map::enum_map![
|
||||
TokenType::Comment => TextFormat::simple(text_style, Color32::from_gray(120)),
|
||||
TokenType::Keyword => TextFormat::simple(text_style, Color32::from_rgb(255, 100, 100)),
|
||||
TokenType::Literal => TextFormat::simple(text_style, Color32::from_rgb(87, 165, 171)),
|
||||
TokenType::StringLiteral => TextFormat::simple(text_style, Color32::from_rgb(109, 147, 226)),
|
||||
TokenType::Punctuation => TextFormat::simple(text_style, Color32::LIGHT_GRAY),
|
||||
TokenType::Whitespace => TextFormat::simple(text_style, Color32::TRANSPARENT),
|
||||
TokenType::Comment => TextFormat::simple(font_id.clone(), Color32::from_gray(120)),
|
||||
TokenType::Keyword => TextFormat::simple(font_id.clone(), Color32::from_rgb(255, 100, 100)),
|
||||
TokenType::Literal => TextFormat::simple(font_id.clone(), Color32::from_rgb(87, 165, 171)),
|
||||
TokenType::StringLiteral => TextFormat::simple(font_id.clone(), Color32::from_rgb(109, 147, 226)),
|
||||
TokenType::Punctuation => TextFormat::simple(font_id.clone(), Color32::LIGHT_GRAY),
|
||||
TokenType::Whitespace => TextFormat::simple(font_id.clone(), Color32::TRANSPARENT),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn light() -> Self {
|
||||
let text_style = egui::TextStyle::Monospace;
|
||||
let font_id = egui::FontId::monospace(12.0);
|
||||
use egui::{Color32, TextFormat};
|
||||
Self {
|
||||
dark_mode: false,
|
||||
#[cfg(not(feature = "syntect"))]
|
||||
formats: enum_map::enum_map![
|
||||
TokenType::Comment => TextFormat::simple(text_style, Color32::GRAY),
|
||||
TokenType::Keyword => TextFormat::simple(text_style, Color32::from_rgb(235, 0, 0)),
|
||||
TokenType::Literal => TextFormat::simple(text_style, Color32::from_rgb(153, 134, 255)),
|
||||
TokenType::StringLiteral => TextFormat::simple(text_style, Color32::from_rgb(37, 203, 105)),
|
||||
TokenType::Punctuation => TextFormat::simple(text_style, Color32::DARK_GRAY),
|
||||
TokenType::Whitespace => TextFormat::simple(text_style, Color32::TRANSPARENT),
|
||||
TokenType::Comment => TextFormat::simple(font_id.clone(), Color32::GRAY),
|
||||
TokenType::Keyword => TextFormat::simple(font_id.clone(), Color32::from_rgb(235, 0, 0)),
|
||||
TokenType::Literal => TextFormat::simple(font_id.clone(), Color32::from_rgb(153, 134, 255)),
|
||||
TokenType::StringLiteral => TextFormat::simple(font_id.clone(), Color32::from_rgb(37, 203, 105)),
|
||||
TokenType::Punctuation => TextFormat::simple(font_id.clone(), Color32::DARK_GRAY),
|
||||
TokenType::Whitespace => TextFormat::simple(font_id.clone(), Color32::TRANSPARENT),
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -237,8 +231,7 @@ impl CodeTheme {
|
||||
ui.horizontal_top(|ui| {
|
||||
let selected_id = egui::Id::null();
|
||||
let mut selected_tt: TokenType = *ui
|
||||
.memory()
|
||||
.data
|
||||
.data()
|
||||
.get_persisted_mut_or(selected_id, TokenType::Comment);
|
||||
|
||||
ui.vertical(|ui| {
|
||||
@@ -259,7 +252,7 @@ impl CodeTheme {
|
||||
// (TokenType::Whitespace, "whitespace"),
|
||||
] {
|
||||
let format = &mut self.formats[tt];
|
||||
ui.style_mut().override_text_style = Some(format.style);
|
||||
ui.style_mut().override_font_id = Some(format.font_id.clone());
|
||||
ui.visuals_mut().override_text_color = Some(format.color);
|
||||
ui.radio_value(&mut selected_tt, tt, tt_name);
|
||||
}
|
||||
@@ -281,7 +274,7 @@ impl CodeTheme {
|
||||
|
||||
ui.add_space(16.0);
|
||||
|
||||
ui.memory().data.insert_persisted(selected_id, selected_tt);
|
||||
ui.data().insert_persisted(selected_id, selected_tt);
|
||||
|
||||
egui::Frame::group(ui.style())
|
||||
.margin(egui::Vec2::splat(2.0))
|
||||
@@ -325,7 +318,7 @@ impl Highligher {
|
||||
// Fallback:
|
||||
LayoutJob::simple(
|
||||
code.into(),
|
||||
egui::TextStyle::Monospace,
|
||||
egui::FontId::monospace(14.0),
|
||||
if theme.dark_mode {
|
||||
egui::Color32::LIGHT_GRAY
|
||||
} else {
|
||||
@@ -371,7 +364,7 @@ impl Highligher {
|
||||
leading_space: 0.0,
|
||||
byte_range: as_byte_range(text, range),
|
||||
format: TextFormat {
|
||||
style: egui::TextStyle::Monospace,
|
||||
font_id: egui::FontId::monospace(14.0),
|
||||
color: text_color,
|
||||
italics,
|
||||
underline,
|
||||
@@ -412,7 +405,7 @@ impl Highligher {
|
||||
while !text.is_empty() {
|
||||
if text.starts_with("//") {
|
||||
let end = text.find('\n').unwrap_or_else(|| text.len());
|
||||
job.append(&text[..end], 0.0, theme.formats[TokenType::Comment]);
|
||||
job.append(&text[..end], 0.0, theme.formats[TokenType::Comment].clone());
|
||||
text = &text[end..];
|
||||
} else if text.starts_with('"') {
|
||||
let end = text[1..]
|
||||
@@ -420,7 +413,11 @@ impl Highligher {
|
||||
.map(|i| i + 2)
|
||||
.or_else(|| text.find('\n'))
|
||||
.unwrap_or_else(|| text.len());
|
||||
job.append(&text[..end], 0.0, theme.formats[TokenType::StringLiteral]);
|
||||
job.append(
|
||||
&text[..end],
|
||||
0.0,
|
||||
theme.formats[TokenType::StringLiteral].clone(),
|
||||
);
|
||||
text = &text[end..];
|
||||
} else if text.starts_with(|c: char| c.is_ascii_alphanumeric()) {
|
||||
let end = text[1..]
|
||||
@@ -432,19 +429,27 @@ impl Highligher {
|
||||
} else {
|
||||
TokenType::Literal
|
||||
};
|
||||
job.append(word, 0.0, theme.formats[tt]);
|
||||
job.append(word, 0.0, theme.formats[tt].clone());
|
||||
text = &text[end..];
|
||||
} else if text.starts_with(|c: char| c.is_ascii_whitespace()) {
|
||||
let end = text[1..]
|
||||
.find(|c: char| !c.is_ascii_whitespace())
|
||||
.map_or_else(|| text.len(), |i| i + 1);
|
||||
job.append(&text[..end], 0.0, theme.formats[TokenType::Whitespace]);
|
||||
job.append(
|
||||
&text[..end],
|
||||
0.0,
|
||||
theme.formats[TokenType::Whitespace].clone(),
|
||||
);
|
||||
text = &text[end..];
|
||||
} else {
|
||||
let mut it = text.char_indices();
|
||||
it.next();
|
||||
let end = it.next().map_or(text.len(), |(idx, _chr)| idx);
|
||||
job.append(&text[..end], 0.0, theme.formats[TokenType::Punctuation]);
|
||||
job.append(
|
||||
&text[..end],
|
||||
0.0,
|
||||
theme.formats[TokenType::Punctuation].clone(),
|
||||
);
|
||||
text = &text[end..];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ impl epi::App for WrapApp {
|
||||
|
||||
fn setup(
|
||||
&mut self,
|
||||
_ctx: &egui::CtxRef,
|
||||
_ctx: &egui::Context,
|
||||
_frame: &epi::Frame,
|
||||
_storage: Option<&dyn epi::Storage>,
|
||||
) {
|
||||
@@ -67,12 +67,7 @@ impl epi::App for WrapApp {
|
||||
egui::Rgba::TRANSPARENT // we set a `CentralPanel` fill color in `demo_windows.rs`
|
||||
}
|
||||
|
||||
fn warm_up_enabled(&self) -> bool {
|
||||
// The example windows use a lot of emojis. Pre-cache them by running one frame where everything is open
|
||||
cfg!(not(debug_assertions))
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &epi::Frame) {
|
||||
if let Some(web_info) = frame.info().web_info.as_ref() {
|
||||
if let Some(anchor) = web_info.web_location_hash.strip_prefix('#') {
|
||||
self.selected_anchor = anchor.to_owned();
|
||||
@@ -165,7 +160,7 @@ impl WrapApp {
|
||||
});
|
||||
}
|
||||
|
||||
fn ui_file_drag_and_drop(&mut self, ctx: &egui::CtxRef) {
|
||||
fn ui_file_drag_and_drop(&mut self, ctx: &egui::Context) {
|
||||
use egui::*;
|
||||
|
||||
// Preview hovering files:
|
||||
@@ -190,7 +185,7 @@ impl WrapApp {
|
||||
screen_rect.center(),
|
||||
Align2::CENTER_CENTER,
|
||||
text,
|
||||
TextStyle::Heading,
|
||||
TextStyle::Heading.resolve(&ctx.style()),
|
||||
Color32::WHITE,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user