mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 21:30:03 -04:00
Merge example_web into egui_demo_lib
This commit is contained in:
@@ -173,21 +173,20 @@ impl FrameHistory {
|
||||
///
|
||||
/// Implements `epi::App` so it can be used with
|
||||
/// [`egui_glium`](https://crates.io/crates/egui_glium) and [`egui_web`](https://crates.io/crates/egui_web).
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct DemoApp {
|
||||
demo_windows: crate::DemoWindows,
|
||||
demo_windows: super::DemoWindows,
|
||||
|
||||
backend_window_open: bool,
|
||||
|
||||
#[cfg_attr(feature = "serde", serde(skip))] // go back to `Reactive` mode each time we start
|
||||
#[serde(skip)] // go back to `Reactive` mode each time we start
|
||||
run_mode: RunMode,
|
||||
|
||||
/// current slider value for current gui scale (backend demo only)
|
||||
pixels_per_point: Option<f32>,
|
||||
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
#[serde(skip)]
|
||||
frame_history: FrameHistory,
|
||||
}
|
||||
|
||||
@@ -286,12 +285,10 @@ impl epi::App for DemoApp {
|
||||
"Egui Demo"
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde_json")]
|
||||
fn load(&mut self, storage: &dyn epi::Storage) {
|
||||
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde_json")]
|
||||
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
||||
epi::set_value(storage, epi::APP_KEY, self);
|
||||
}
|
||||
@@ -308,12 +305,12 @@ impl epi::App for DemoApp {
|
||||
.unwrap_or_default();
|
||||
|
||||
let link = if web_location_hash == "clock" {
|
||||
Some(crate::DemoLink::Clock)
|
||||
Some(super::DemoLink::Clock)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let demo_environment = crate::DemoEnvironment {
|
||||
let demo_environment = super::DemoEnvironment {
|
||||
seconds_since_midnight: frame.info().seconds_since_midnight,
|
||||
link,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
use egui::{containers::*, *};
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct DancingStrings {}
|
||||
|
||||
impl Default for DancingStrings {
|
||||
@@ -10,13 +10,13 @@ impl Default for DancingStrings {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Demo for DancingStrings {
|
||||
impl super::Demo for DancingStrings {
|
||||
fn name(&self) -> &str {
|
||||
"♫ Dancing Strings"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &CtxRef, open: &mut bool) {
|
||||
use crate::View;
|
||||
use super::View;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
.default_size(vec2(512.0, 256.0))
|
||||
@@ -25,7 +25,7 @@ impl crate::Demo for DancingStrings {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::View for DancingStrings {
|
||||
impl super::View for DancingStrings {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
Frame::dark_canvas(ui.style()).show(ui, |ui| {
|
||||
ui.ctx().request_repaint();
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::*;
|
||||
use super::*;
|
||||
use egui::{color::*, *};
|
||||
|
||||
/// Showcase some ui code
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct DemoWindow {
|
||||
num_columns: usize,
|
||||
|
||||
@@ -106,8 +106,8 @@ impl DemoWindow {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
struct ColorWidgets {
|
||||
srgba_unmul: [u8; 4],
|
||||
srgba_premul: [u8; 4],
|
||||
@@ -178,8 +178,8 @@ impl ColorWidgets {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
struct BoxPainting {
|
||||
size: Vec2,
|
||||
corner_radius: f32,
|
||||
@@ -222,8 +222,8 @@ impl BoxPainting {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
struct LayoutDemo {
|
||||
// Identical to contents of `egui::Layout`
|
||||
main_dir: Direction,
|
||||
@@ -365,8 +365,7 @@ enum Action {
|
||||
Delete,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(Clone, Default, serde::Deserialize, serde::Serialize)]
|
||||
struct Tree(Vec<Tree>);
|
||||
|
||||
impl Tree {
|
||||
@@ -20,22 +20,22 @@ pub struct DemoEnvironment {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
struct Demos {
|
||||
/// open, view
|
||||
#[cfg_attr(feature = "serde", serde(skip))] // TODO: serialize the `open` state.
|
||||
demos: Vec<(bool, Box<dyn crate::Demo>)>,
|
||||
#[serde(skip)] // TODO: serialize the `open` state.
|
||||
demos: Vec<(bool, Box<dyn super::Demo>)>,
|
||||
}
|
||||
impl Default for Demos {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
demos: vec![
|
||||
(false, Box::new(crate::FontBook::default())),
|
||||
(false, Box::new(crate::Painting::default())),
|
||||
(false, Box::new(crate::DancingStrings::default())),
|
||||
(false, Box::new(crate::DragAndDropDemo::default())),
|
||||
(false, Box::new(crate::Tests::default())),
|
||||
(false, Box::new(super::FontBook::default())),
|
||||
(false, Box::new(super::Painting::default())),
|
||||
(false, Box::new(super::DancingStrings::default())),
|
||||
(false, Box::new(super::DragAndDropDemo::default())),
|
||||
(false, Box::new(super::Tests::default())),
|
||||
],
|
||||
}
|
||||
}
|
||||
@@ -57,23 +57,22 @@ impl Demos {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// A menu bar in which you can select different demo windows to show.
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct DemoWindows {
|
||||
open_windows: OpenWindows,
|
||||
|
||||
demo_window: crate::DemoWindow,
|
||||
demo_window: super::DemoWindow,
|
||||
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
color_test: crate::ColorTest,
|
||||
#[serde(skip)]
|
||||
color_test: super::ColorTest,
|
||||
|
||||
fractal_clock: crate::FractalClock,
|
||||
fractal_clock: super::FractalClock,
|
||||
|
||||
/// open, title, view
|
||||
demos: Demos,
|
||||
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
#[serde(skip)]
|
||||
previous_link: Option<DemoLink>,
|
||||
}
|
||||
|
||||
@@ -257,7 +256,7 @@ impl DemoWindows {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
struct OpenWindows {
|
||||
demo: bool,
|
||||
fractal_clock: bool,
|
||||
@@ -89,13 +89,13 @@ impl Default for DragAndDropDemo {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Demo for DragAndDropDemo {
|
||||
impl super::Demo for DragAndDropDemo {
|
||||
fn name(&self) -> &str {
|
||||
"✋ Drag and Drop"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &CtxRef, open: &mut bool) {
|
||||
use crate::View;
|
||||
use super::View;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
.default_size(vec2(256.0, 256.0))
|
||||
@@ -105,7 +105,7 @@ impl crate::Demo for DragAndDropDemo {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::View for DragAndDropDemo {
|
||||
impl super::View for DragAndDropDemo {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
ui.label("This is a proof-of-concept of drag-and-drop in Egui");
|
||||
ui.label("Drag items between columns.");
|
||||
@@ -39,23 +39,23 @@ impl FontBook {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Demo for FontBook {
|
||||
impl super::Demo for FontBook {
|
||||
fn name(&self) -> &str {
|
||||
"🔤 Font Book"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
egui::Window::new(self.name()).open(open).show(ctx, |ui| {
|
||||
use crate::View;
|
||||
use super::View;
|
||||
self.ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::View for FontBook {
|
||||
impl super::View for FontBook {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
use crate::font_contents_emoji::FULL_EMOJI_LIST;
|
||||
use crate::font_contents_ubuntu::UBUNTU_FONT_CHARACTERS;
|
||||
use super::font_contents_emoji::FULL_EMOJI_LIST;
|
||||
use super::font_contents_ubuntu::UBUNTU_FONT_CHARACTERS;
|
||||
|
||||
ui.label(format!(
|
||||
"Egui supports {} standard characters and {} emojis.\nClick on a character to copy it.",
|
||||
@@ -2,6 +2,7 @@
|
||||
///
|
||||
/// Also viewable at <http://jslegers.github.io/emoji-icon-font/>
|
||||
/// Source: <https://github.com/jslegers/emoji-icon-font>
|
||||
#[allow(unused)]
|
||||
#[rustfmt::skip]
|
||||
pub const EMOJI_ICON_FONT_LIST: &[(u32, char, &str)] = &[
|
||||
(0xA9, '©', "copyright"),
|
||||
@@ -652,6 +653,7 @@ pub const EMOJI_ICON_FONT_LIST: &[(u32, char, &str)] = &[
|
||||
(0x1F6E1, '🛡', "shield"),
|
||||
];
|
||||
|
||||
#[allow(unused)]
|
||||
#[rustfmt::skip]
|
||||
pub const NOTO_EMOJI_LIST: &[(u32, char, &str)] = &[
|
||||
(0xA9, '©', "copyright sign"),
|
||||
@@ -1,8 +1,8 @@
|
||||
use egui::{containers::*, widgets::*, *};
|
||||
use std::f32::consts::TAU;
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct FractalClock {
|
||||
paused: bool,
|
||||
time: f64,
|
||||
43
egui_demo_lib/src/apps/demo/mod.rs
Normal file
43
egui_demo_lib/src/apps/demo/mod.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
//! Demo-code for showing how Egui is used.
|
||||
//!
|
||||
//! The demo-code is also used in benchmarks and tests.
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
mod app;
|
||||
mod color_test;
|
||||
mod dancing_strings;
|
||||
pub mod demo_window;
|
||||
mod demo_windows;
|
||||
mod drag_and_drop;
|
||||
mod font_book;
|
||||
pub mod font_contents_emoji;
|
||||
pub mod font_contents_ubuntu;
|
||||
mod fractal_clock;
|
||||
mod painting;
|
||||
mod scrolls;
|
||||
mod sliders;
|
||||
mod tests;
|
||||
pub mod toggle_switch;
|
||||
mod widgets;
|
||||
|
||||
pub use {
|
||||
app::*, color_test::ColorTest, dancing_strings::DancingStrings, demo_window::DemoWindow,
|
||||
demo_windows::*, drag_and_drop::*, font_book::FontBook, fractal_clock::FractalClock,
|
||||
painting::Painting, scrolls::Scrolls, sliders::Sliders, tests::Tests, widgets::Widgets,
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Something to view in the demo windows
|
||||
pub trait View {
|
||||
fn ui(&mut self, ui: &mut egui::Ui);
|
||||
}
|
||||
|
||||
/// Something to view
|
||||
pub trait Demo {
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Show windows, etc
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool);
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::*;
|
||||
use egui::*;
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct Painting {
|
||||
lines: Vec<Vec<Vec2>>,
|
||||
stroke: Stroke,
|
||||
@@ -59,12 +58,13 @@ impl Painting {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Demo for Painting {
|
||||
impl super::Demo for Painting {
|
||||
fn name(&self) -> &str {
|
||||
"🖊 Painting"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &CtxRef, open: &mut bool) {
|
||||
use super::View;
|
||||
Window::new(self.name())
|
||||
.open(open)
|
||||
.default_size(vec2(512.0, 512.0))
|
||||
@@ -73,7 +73,7 @@ impl crate::Demo for Painting {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::View for Painting {
|
||||
impl super::View for Painting {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
ui.add(crate::__egui_github_link_file!("(source code)"));
|
||||
self.ui_control(ui);
|
||||
@@ -1,8 +1,7 @@
|
||||
use crate::LOREM_IPSUM_LONG;
|
||||
use egui::{color::*, *};
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct Scrolls {
|
||||
track_item: usize,
|
||||
tracking: bool,
|
||||
@@ -22,8 +21,8 @@ impl Default for Scrolls {
|
||||
impl Scrolls {
|
||||
pub fn ui(&mut self, ui: &mut Ui) {
|
||||
ScrollArea::from_max_height(200.0).show(ui, |ui| {
|
||||
ui.label(LOREM_IPSUM_LONG);
|
||||
ui.label(LOREM_IPSUM_LONG);
|
||||
ui.label(crate::LOREM_IPSUM_LONG);
|
||||
ui.label(crate::LOREM_IPSUM_LONG);
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
@@ -2,8 +2,8 @@ use egui::*;
|
||||
use std::f64::INFINITY;
|
||||
|
||||
/// Showcase sliders
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct Sliders {
|
||||
pub min: f64,
|
||||
pub max: f64,
|
||||
@@ -1,20 +1,20 @@
|
||||
#[derive(Default)]
|
||||
pub struct Tests {}
|
||||
|
||||
impl crate::Demo for Tests {
|
||||
impl super::Demo for Tests {
|
||||
fn name(&self) -> &str {
|
||||
"📋 Tests"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
egui::Window::new(self.name()).open(open).show(ctx, |ui| {
|
||||
use crate::View;
|
||||
use super::View;
|
||||
self.ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::View for Tests {
|
||||
impl super::View for Tests {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.heading("Name collision example");
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use egui::{color::*, *};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
enum Enum {
|
||||
First,
|
||||
Second,
|
||||
@@ -14,13 +13,13 @@ impl Default for Enum {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct Widgets {
|
||||
button_enabled: bool,
|
||||
count: usize,
|
||||
radio: Enum,
|
||||
sliders: crate::Sliders,
|
||||
sliders: super::Sliders,
|
||||
angle: f32,
|
||||
color: Srgba,
|
||||
single_line_text_input: String,
|
||||
@@ -154,6 +153,6 @@ impl Widgets {
|
||||
ui.text_edit_multiline(&mut self.multiline_text_input);
|
||||
|
||||
ui.separator();
|
||||
crate::toggle_switch::demo(ui, &mut self.toggle_switch);
|
||||
super::toggle_switch::demo(ui, &mut self.toggle_switch);
|
||||
}
|
||||
}
|
||||
296
egui_demo_lib/src/apps/http_app.rs
Normal file
296
egui_demo_lib/src/apps/http_app.rs
Normal file
@@ -0,0 +1,296 @@
|
||||
use epi::http::Response;
|
||||
use std::sync::mpsc::Receiver;
|
||||
|
||||
struct Resource {
|
||||
/// HTTP response
|
||||
response: Response,
|
||||
|
||||
/// If set, the response was an image.
|
||||
image: Option<Image>,
|
||||
|
||||
/// If set, the response was text with some supported syntax highlighting (e.g. ".rs" or ".md").
|
||||
colored_text: Option<ColoredText>,
|
||||
}
|
||||
|
||||
impl Resource {
|
||||
fn from_response(response: Response) -> Self {
|
||||
let image = if response.header_content_type.starts_with("image/") {
|
||||
Image::decode(&response.bytes)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let colored_text = syntax_highlighting(&response);
|
||||
|
||||
Self {
|
||||
response,
|
||||
image,
|
||||
colored_text,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub struct HttpApp {
|
||||
url: String,
|
||||
|
||||
#[serde(skip)]
|
||||
in_progress: Option<Receiver<Result<Response, String>>>,
|
||||
|
||||
#[serde(skip)]
|
||||
result: Option<Result<Resource, String>>,
|
||||
|
||||
#[serde(skip)]
|
||||
tex_mngr: TexMngr,
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl epi::App for HttpApp {
|
||||
fn name(&self) -> &str {
|
||||
"HTTP Fetch"
|
||||
}
|
||||
|
||||
/// Called each time the UI needs repainting, which may be many times per second.
|
||||
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
|
||||
fn ui(&mut self, ctx: &egui::CtxRef, frame: &mut 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(Resource::from_response));
|
||||
}
|
||||
}
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Egui Fetch Example");
|
||||
ui.add(egui::github_link_file!(
|
||||
"https://github.com/emilk/egui/blob/master/",
|
||||
"(source code)"
|
||||
));
|
||||
|
||||
if let Some(url) = ui_url(ui, &mut self.url) {
|
||||
let repaint_signal = frame.repaint_signal();
|
||||
let (sender, receiver) = std::sync::mpsc::channel();
|
||||
self.in_progress = Some(receiver);
|
||||
|
||||
frame.http_fetch(epi::http::Request::get(url), move |response| {
|
||||
sender.send(response).ok();
|
||||
repaint_signal.request_repaint();
|
||||
});
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
if self.in_progress.is_some() {
|
||||
ui.label("Please wait...");
|
||||
} else if let Some(result) = &self.result {
|
||||
match result {
|
||||
Ok(resource) => {
|
||||
ui_resouce(ui, frame, &mut self.tex_mngr, resource);
|
||||
}
|
||||
Err(error) => {
|
||||
// This should only happen if the fetch API isn't available or something similar.
|
||||
ui.add(egui::Label::new(error).text_color(egui::color::RED));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn ui_url(ui: &mut egui::Ui, url: &mut String) -> Option<String> {
|
||||
let mut trigger_fetch = false;
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("URL:");
|
||||
trigger_fetch |= ui.text_edit_singleline(url).lost_kb_focus;
|
||||
trigger_fetch |= ui.button("GET").clicked;
|
||||
});
|
||||
|
||||
ui.label("HINT: paste the url of this page into the field above!");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Source code for this example").clicked {
|
||||
*url = format!(
|
||||
"https://raw.githubusercontent.com/emilk/egui/master/{}",
|
||||
file!()
|
||||
);
|
||||
trigger_fetch = true;
|
||||
}
|
||||
if ui.button("Random image").clicked {
|
||||
let seed = ui.input().time;
|
||||
let width = 640;
|
||||
let height = 480;
|
||||
*url = format!("https://picsum.photos/seed/{}/{}/{}", seed, width, height);
|
||||
trigger_fetch = true;
|
||||
}
|
||||
});
|
||||
|
||||
if trigger_fetch {
|
||||
Some(url.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn ui_resouce(
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut epi::Frame<'_>,
|
||||
tex_mngr: &mut TexMngr,
|
||||
resource: &Resource,
|
||||
) {
|
||||
let Resource {
|
||||
response,
|
||||
image,
|
||||
colored_text,
|
||||
} = resource;
|
||||
|
||||
ui.monospace(format!("url: {}", response.url));
|
||||
ui.monospace(format!(
|
||||
"status: {} ({})",
|
||||
response.status, response.status_text
|
||||
));
|
||||
ui.monospace(format!("Content-Type: {}", response.header_content_type));
|
||||
ui.monospace(format!(
|
||||
"Size: {:.1} kB",
|
||||
response.bytes.len() as f32 / 1000.0
|
||||
));
|
||||
|
||||
if let Some(text) = &response.text {
|
||||
let tooltip = "Click to copy the response body";
|
||||
if ui.button("📋").on_hover_text(tooltip).clicked {
|
||||
ui.output().copied_text = text.clone();
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
egui::ScrollArea::auto_sized().show(ui, |ui| {
|
||||
if let Some(image) = image {
|
||||
if let Some(texture_id) = tex_mngr.texture(frame, &response.url, image) {
|
||||
let size = egui::Vec2::new(image.size.0 as f32, image.size.1 as f32);
|
||||
ui.image(texture_id, size);
|
||||
}
|
||||
} else if let Some(colored_text) = colored_text {
|
||||
colored_text.ui(ui);
|
||||
} else if let Some(text) = &response.text {
|
||||
ui.monospace(text);
|
||||
} else {
|
||||
ui.monospace("[binary]");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Syntax highlighting:
|
||||
|
||||
fn syntax_highlighting(response: &Response) -> Option<ColoredText> {
|
||||
let text = response.text.as_ref()?;
|
||||
let extension_and_rest: Vec<&str> = response.url.rsplitn(2, '.').collect();
|
||||
let extension = extension_and_rest.get(0)?;
|
||||
ColoredText::text_with_extension(text, extension)
|
||||
}
|
||||
|
||||
/// Lines of text fragments
|
||||
struct ColoredText(Vec<Vec<(syntect::highlighting::Style, String)>>);
|
||||
|
||||
impl ColoredText {
|
||||
/// e.g. `text_with_extension("fn foo() {}", "rs")`
|
||||
pub fn text_with_extension(text: &str, extension: &str) -> Option<ColoredText> {
|
||||
use syntect::easy::HighlightLines;
|
||||
use syntect::highlighting::ThemeSet;
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use syntect::util::LinesWithEndings;
|
||||
|
||||
let ps = SyntaxSet::load_defaults_newlines(); // should be cached and reused
|
||||
let ts = ThemeSet::load_defaults(); // should be cached and reused
|
||||
|
||||
let syntax = ps.find_syntax_by_extension(extension)?;
|
||||
|
||||
let mut h = HighlightLines::new(syntax, &ts.themes["base16-mocha.dark"]);
|
||||
|
||||
let lines = LinesWithEndings::from(text)
|
||||
.map(|line| {
|
||||
h.highlight(line, &ps)
|
||||
.into_iter()
|
||||
.map(|(style, range)| (style, range.trim_end_matches('\n').to_owned()))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some(ColoredText(lines))
|
||||
}
|
||||
|
||||
pub fn ui(&self, ui: &mut egui::Ui) {
|
||||
for line in &self.0 {
|
||||
ui.horizontal_wrapped_for_text(egui::TextStyle::Monospace, |ui| {
|
||||
ui.style_mut().spacing.item_spacing.x = 0.0;
|
||||
for (style, range) in line {
|
||||
let fg = style.foreground;
|
||||
let text_color = egui::Srgba::from_rgb(fg.r, fg.g, fg.b);
|
||||
ui.add(egui::Label::new(range).monospace().text_color(text_color));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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: &mut epi::Frame<'_>,
|
||||
url: &str,
|
||||
image: &Image,
|
||||
) -> Option<egui::TextureId> {
|
||||
let tex_allocator = frame.tex_allocator().as_mut()?;
|
||||
let texture_id = self.texture_id.unwrap_or_else(|| tex_allocator.alloc());
|
||||
self.texture_id = Some(texture_id);
|
||||
if self.loaded_url != url {
|
||||
self.loaded_url = url.to_owned();
|
||||
tex_allocator.set_srgba_premultiplied(texture_id, image.size, &image.pixels);
|
||||
}
|
||||
Some(texture_id)
|
||||
}
|
||||
}
|
||||
|
||||
struct Image {
|
||||
size: (usize, usize),
|
||||
pixels: Vec<egui::Srgba>,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
fn decode(bytes: &[u8]) -> Option<Image> {
|
||||
use image::GenericImageView;
|
||||
let image = image::load_from_memory(bytes).ok()?;
|
||||
let image_buffer = image.to_rgba8();
|
||||
let size = (image.width() as usize, image.height() as usize);
|
||||
let pixels = image_buffer.into_vec();
|
||||
assert_eq!(size.0 * size.1 * 4, pixels.len());
|
||||
let pixels = pixels
|
||||
.chunks(4)
|
||||
.map(|p| egui::Srgba::from_rgba_unmultiplied(p[0], p[1], p[2], p[3]))
|
||||
.collect();
|
||||
|
||||
Some(Image { size, pixels })
|
||||
}
|
||||
}
|
||||
7
egui_demo_lib/src/apps/mod.rs
Normal file
7
egui_demo_lib/src/apps/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
mod demo;
|
||||
mod http_app;
|
||||
|
||||
pub use demo::DemoApp;
|
||||
pub use http_app::HttpApp;
|
||||
|
||||
pub use demo::DemoWindows; // used for tests
|
||||
@@ -45,51 +45,11 @@
|
||||
unused_doc_comments,
|
||||
)]
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
mod apps;
|
||||
mod wrap_app;
|
||||
|
||||
mod app;
|
||||
mod color_test;
|
||||
mod dancing_strings;
|
||||
pub mod demo_window;
|
||||
mod demo_windows;
|
||||
mod drag_and_drop;
|
||||
mod font_book;
|
||||
pub mod font_contents_emoji;
|
||||
pub mod font_contents_ubuntu;
|
||||
mod fractal_clock;
|
||||
mod painting;
|
||||
mod scrolls;
|
||||
mod sliders;
|
||||
mod tests;
|
||||
pub mod toggle_switch;
|
||||
mod widgets;
|
||||
|
||||
pub use {
|
||||
app::*, color_test::ColorTest, dancing_strings::DancingStrings, demo_window::DemoWindow,
|
||||
demo_windows::*, drag_and_drop::*, font_book::FontBook, fractal_clock::FractalClock,
|
||||
painting::Painting, scrolls::Scrolls, sliders::Sliders, tests::Tests, widgets::Widgets,
|
||||
};
|
||||
|
||||
pub const LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
||||
|
||||
pub const LOREM_IPSUM_LONG: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
|
||||
Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst.";
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Something to view in the demo windows
|
||||
pub trait View {
|
||||
fn ui(&mut self, ui: &mut egui::Ui);
|
||||
}
|
||||
|
||||
/// Something to view
|
||||
pub trait Demo {
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Show windows, etc
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool);
|
||||
}
|
||||
pub use apps::DemoWindows; // used for tests
|
||||
pub use wrap_app::WrapApp;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -119,6 +79,14 @@ macro_rules! __egui_github_link_file_line {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
pub const LOREM_IPSUM: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
||||
|
||||
pub const LOREM_IPSUM_LONG: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||
|
||||
Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst.";
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[test]
|
||||
fn test_egui_e2e() {
|
||||
let mut demo_windows = crate::DemoWindows::default();
|
||||
|
||||
67
egui_demo_lib/src/wrap_app.rs
Normal file
67
egui_demo_lib/src/wrap_app.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
/// Wraps many demo/test apps into one
|
||||
#[derive(Default, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct WrapApp {
|
||||
selectable_demo_name: String,
|
||||
|
||||
demo: crate::apps::DemoApp,
|
||||
http: crate::apps::HttpApp,
|
||||
}
|
||||
|
||||
impl epi::App for WrapApp {
|
||||
fn name(&self) -> &str {
|
||||
"Egui Demo Apps"
|
||||
}
|
||||
|
||||
fn load(&mut self, storage: &dyn epi::Storage) {
|
||||
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
||||
}
|
||||
|
||||
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
||||
epi::set_value(storage, epi::APP_KEY, self);
|
||||
}
|
||||
|
||||
fn ui(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) {
|
||||
let web_location_hash = frame
|
||||
.info()
|
||||
.web_info
|
||||
.as_ref()
|
||||
.map(|info| info.web_location_hash.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
if web_location_hash == "#clock" {
|
||||
// TODO
|
||||
} else if web_location_hash == "#http" {
|
||||
self.selectable_demo_name = self.http.name().to_owned();
|
||||
}
|
||||
|
||||
egui::TopPanel::top("wrap_app").show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!("web_location_hash: {:?}", web_location_hash));
|
||||
ui.label("Demo Apps:");
|
||||
ui.selectable_value(
|
||||
&mut self.selectable_demo_name,
|
||||
self.demo.name().to_owned(),
|
||||
self.demo.name(),
|
||||
);
|
||||
ui.selectable_value(
|
||||
&mut self.selectable_demo_name,
|
||||
self.http.name().to_owned(),
|
||||
self.http.name(),
|
||||
);
|
||||
|
||||
ui.with_layout(egui::Layout::right_to_left(), |ui| {
|
||||
egui::warn_if_debug_build(ui);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if self.selectable_demo_name == self.demo.name() {
|
||||
self.demo.ui(ctx, frame);
|
||||
} else if self.selectable_demo_name == self.http.name() {
|
||||
self.http.ui(ctx, frame);
|
||||
} else {
|
||||
self.selectable_demo_name = self.demo.name().to_owned();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user