Skip to content

Commit

Permalink
Editor: update to egui 0.31 and use semi-custom modals
Browse files Browse the repository at this point in the history
  • Loading branch information
barsoosayque committed Feb 9, 2025
1 parent 9b29780 commit 11843a8
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 173 deletions.
15 changes: 8 additions & 7 deletions crates/opensi-editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,31 @@ wayland = ["eframe/wayland"]
[dependencies]
opensi-core = { version = "*", path = "../opensi-core" }

egui = "0.30"
eframe = { version = "0.30", default-features = false, features = [
egui = "0.31"
eframe = { version = "0.31", default-features = false, features = [
"glow", # Use the glow rendering backend. Alternative: "wgpu".
"persistence", # Enable restoring app state when restarting the app.
] }
egui-phosphor = { version = "0.8", default-features = false, features = ["fill"] }
log = "0.4"
egui-phosphor = { version = "0.9", default-features = false, features = ["fill"] }
egui_extras = "0.31.0"
rfd = { version = "0.15.2", default-features = false, features = ["xdg-portal", "tokio"] }
thiserror = "2.0.11"
serde = { version = "1", features = ["derive"] }
egui_extras = "0.30.0"
itertools = "0.14.0"
egui-modal = "0.6.0"
const_format = "0.2.34"
bincode = "1.3.3"
log = "0.4"

# native:
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
env_logger = "0.11"
dirs = "6.0.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync"] }
humantime = "2.1.0"
fern = { version = "0.7.1", features = ["colored"] }

# web:
[target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { version = "1", features = ["macros", "rt"] }
tokio_with_wasm = { version = "0.7.4", features = ["macros", "rt", "sync"] }
web-sys = "0.3.77" # to access the DOM (to hide the loading text)
fern = { version = "0.7.1" }
7 changes: 5 additions & 2 deletions crates/opensi-editor/src/app/file_dialogs.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::path::Path;

use log::error;
use opensi_core::prelude::*;
#[cfg(not(target_arch = "wasm32"))]
use tokio;
Expand Down Expand Up @@ -28,8 +29,10 @@ pub fn import_dialog() -> LoadingPackageReceiver {
let (sender, receiver) = tokio::sync::oneshot::channel();
let _handle = tokio::spawn(async {
let package = import_package().await;
// TODO: handle channel errors ?
let _ = sender.send(package).unwrap();
match sender.send(package) {
Ok(_) => {},
Err(_) => error!("Error sending imported package !"),
};
});
receiver
}
Expand Down
124 changes: 59 additions & 65 deletions crates/opensi-editor/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ use itertools::Itertools;
use log::error;
use opensi_core::prelude::*;

use crate::{app::file_dialogs::LoadingPackageReceiver, icon_format, icon_str, icon_string, style};
use crate::{
app::file_dialogs::LoadingPackageReceiver,
element::{ModalExt, ModalWrapper},
icon_format, icon_str, icon_string, style,
};

const FONT_REGULAR_ID: &'static str = "regular";

Expand Down Expand Up @@ -85,60 +89,8 @@ impl eframe::App for EditorApp {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
self.package_state.update();

let new_pack_modal =
egui_modal::Modal::new(ctx, "new-pack-modal").with_close_on_outside_click(true);
new_pack_modal.show(|ui| {
new_pack_modal.title(ui, "Перезаписать текущий пак ?");
new_pack_modal.frame(ui, |ui| {
new_pack_modal
.body(ui, "Создание нового пака перезапишет текущий пак. Вы уверены ?");
});
new_pack_modal.buttons(ui, |ui| {
if new_pack_modal.caution_button(ui, "Отмена").clicked() {
new_pack_modal.close();
};
if new_pack_modal.suggested_button(ui, "Перезаписать").clicked() {
self.package_state =
PackageState::Active { package: Package::new(), selected: None };
};
});
});

let authors_modal =
egui_modal::Modal::new(ctx, "authors-modal").with_close_on_outside_click(true);
authors_modal.show(|ui| {
authors_modal.title(ui, icon_str!(GRADUATION_CAP, "OpenSI Editor"));
authors_modal.frame(ui, |ui| {
ui.horizontal(|ui| {
ui.strong("Авторы:");
let authors = env!("CARGO_PKG_AUTHORS").split(":").join(", ");
ui.label(authors);
});

ui.horizontal(|ui| {
ui.strong("Версия:");
let version = env!("CARGO_PKG_VERSION");
ui.code(format!("v{version}"));
});

ui.horizontal(|ui| {
ui.strong("Репозиторий:");
let url = env!("CARGO_PKG_REPOSITORY");
let short_url = url.trim_start_matches("https://github.com/");
ui.hyperlink_to(icon_format!(GITHUB_LOGO, "{short_url}"), url);
});

ui.separator();

ui.horizontal(|ui| {
ui.weak("Сделано с помощью");
ui.hyperlink_to("egui", "https://github.com/emilk/egui");
});
});
authors_modal.buttons(ui, |ui| {
authors_modal.button(ui, "Закрыть");
});
});
let mut new_pack_modal = ModalWrapper::new(ctx, "new-pack-modal");
let mut authors_modal = ModalWrapper::new(ctx, "authors-modal");

egui::TopBottomPanel::top("top_panel")
.frame(egui::Frame::side_top_panel(&ctx.style()).inner_margin(4.0))
Expand Down Expand Up @@ -254,6 +206,50 @@ impl eframe::App for EditorApp {
},
);
});

new_pack_modal.show(ctx, |ui| {
ui.modal_title(icon_str!(PENCIL_SIMPLE_LINE, "Перезаписать текущий пак ?"));
ui.modal_buttons(|ui| {
if ui.modal_danger(icon_str!(PROHIBIT, "Отмена")).clicked() {}
if ui.modal_confirm(icon_str!(CHECK, "Перезаписать")).clicked() {
self.package_state =
PackageState::Active { package: Package::new(), selected: None };
}
});
});

authors_modal.show(ctx, |ui| {
ui.modal_title(icon_str!(GRADUATION_CAP, "OpenSI Editor"));
ui.horizontal(|ui| {
ui.strong("Авторы:");
let authors = env!("CARGO_PKG_AUTHORS").split(":").join(", ");
ui.label(authors);
});

ui.horizontal(|ui| {
ui.strong("Версия:");
let version = env!("CARGO_PKG_VERSION");
ui.code(format!("v{version}"));
});

ui.horizontal(|ui| {
ui.strong("Репозиторий:");
let url = env!("CARGO_PKG_REPOSITORY");
let short_url = url.trim_start_matches("https://github.com/");
ui.hyperlink_to(icon_format!(GITHUB_LOGO, "{short_url}"), url);
});

ui.separator();

ui.horizontal(|ui| {
ui.weak("Сделано с помощью");
ui.hyperlink_to("egui", "https://github.com/emilk/egui");
});

ui.modal_buttons(|ui| {
ui.modal_button(icon_str!(X, "Закрыть"));
});
});
}
}

Expand All @@ -272,16 +268,14 @@ enum PackageState {
impl PackageState {
fn update(&mut self) {
match self {
Self::Loading(receiver) => {
match receiver.try_recv() {
Ok(Ok(package)) => {
*self = Self::Active { package, selected: None };
},
Ok(Err(_err)) => {
// TODO: error handle
},
Err(_) => {},
}
Self::Loading(receiver) => match receiver.try_recv() {
Ok(Ok(package)) => {
*self = Self::Active { package, selected: None };
},
Ok(Err(err)) => {
error!("Error loading package: {err}");
},
Err(_) => {},
},
_ => {},
}
Expand Down
4 changes: 2 additions & 2 deletions crates/opensi-editor/src/element/card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ impl<'a> egui::Widget for Card<'a> {
(self.style.fill_color(ui.visuals()), self.style.stroke(ui.visuals()));
let mut frame = egui::Frame::default()
.inner_margin(16.0)
.outer_margin(egui::Margin::symmetric(0.0, 4.0))
.outer_margin(egui::Margin::symmetric(0, 4))
.stroke(stroke)
.rounding(8.0)
.corner_radius(8)
.fill(fill_color)
.begin(ui);

Expand Down
6 changes: 3 additions & 3 deletions crates/opensi-editor/src/element/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ pub fn string_list(
let mut deleted_index = None;

for (index, item) in list.iter().enumerate() {
egui::Frame::none()
.rounding(4.0)
.inner_margin(egui::Margin { left: 4.0, ..Default::default() })
egui::Frame::new()
.corner_radius(4)
.inner_margin(egui::Margin { left: 4, ..Default::default() })
.fill(ui.style().visuals.widgets.inactive.bg_fill)
.show(ui, |ui| {
ui.horizontal(|ui| {
Expand Down
2 changes: 2 additions & 0 deletions crates/opensi-editor/src/element/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
pub mod card;
pub mod common;
pub mod modal;
pub mod naming;
pub mod node_context;
pub mod property;
pub mod section;

pub use common::*;
pub use modal::{ModalExt, ModalWrapper};
pub use naming::*;
pub use property::PropertyTable;
pub use section::Sections;
128 changes: 128 additions & 0 deletions crates/opensi-editor/src/element/modal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use super::danger_button;

/// Ergonomic wrapper around egui's Modal with
/// common style.
pub struct ModalWrapper {
id: egui::Id,
inner: egui::Modal,
open: bool,
}

#[allow(dead_code)]
impl ModalWrapper {
pub fn new(ctx: &egui::Context, id: impl std::hash::Hash) -> Self {
let id = egui::Id::new(id);
let inner = egui::Modal::new(id).frame(egui::Frame::popup(&ctx.style()).inner_margin(20));
let open = ctx.memory(|memory| memory.data.get_temp(id.with("open"))).unwrap_or_default();
Self { inner, id, open }
}

pub fn is_open(&self) -> bool {
self.open
}

pub fn set_open(&mut self, open: bool) {
self.open = open;
}

pub fn open(&mut self) {
self.set_open(true);
}

pub fn close(&mut self) {
self.set_open(false);
}

pub fn show(self, ctx: &egui::Context, content: impl FnMut(&mut egui::Ui)) {
let open_id = self.id.with("open");
if self.is_open() {
ctx.memory_mut(|memory| {
memory.data.insert_temp(egui::Id::new("last-modal-id"), open_id);
if memory.data.get_temp::<bool>(open_id).is_none_or(|open| !open) {
memory.data.insert_temp(open_id, true);
}
});
let response = self.inner.show(ctx, content);
if response.should_close() {
ctx.memory_mut(|memory| memory.data.insert_temp(open_id, false));
}
}
}
}

/// Extension methods for ui for [`ModalWrapper`].
pub trait ModalExt {
fn close_modal(self);

fn modal_title(self, title: impl Into<String>) -> egui::Response;

fn modal_buttons(self, content: impl FnMut(&mut egui::Ui)) -> egui::Response;

fn modal_danger(self, button: impl Into<egui::WidgetText>) -> egui::Response;

fn modal_confirm(self, button: impl Into<egui::WidgetText>) -> egui::Response;

fn modal_button(self, button: impl Into<egui::WidgetText>) -> egui::Response;
}

impl ModalExt for &'_ mut egui::Ui {
fn close_modal(self) {
let Some(open_id) =
self.memory(|memory| memory.data.get_temp(egui::Id::new("last-modal-id")))
else {
return;
};
self.memory_mut(|memory| memory.data.insert_temp(open_id, false));
}

fn modal_title(self, title: impl Into<String>) -> egui::Response {
let response = self.add(egui::Label::new(
egui::RichText::new(title)
.heading()
.color(self.visuals().widgets.active.bg_stroke.color),
));
self.separator();
response
}

fn modal_buttons(self, content: impl FnMut(&mut egui::Ui)) -> egui::Response {
self.separator();
self.with_layout(egui::Layout::right_to_left(egui::Align::Center), content).response
}

fn modal_danger(self, button: impl Into<egui::WidgetText>) -> egui::Response {
let response = danger_button(button, self);
if response.clicked() {
self.close_modal();
}
response
}

fn modal_confirm(self, button: impl Into<egui::WidgetText>) -> egui::Response {
let response = self
.scope(|ui| {
let fg = ui.visuals().widgets.active.bg_stroke.color;
let bg = fg.linear_multiply(0.1);

ui.style_mut().visuals.widgets.inactive.fg_stroke.color = fg;
ui.style_mut().visuals.widgets.active.fg_stroke.color = fg;
ui.style_mut().visuals.widgets.hovered.weak_bg_fill = bg;
ui.style_mut().visuals.widgets.hovered.bg_fill = bg;
ui.style_mut().visuals.widgets.hovered.fg_stroke.color = fg;
ui.add(egui::Button::new(button))
})
.inner;
if response.clicked() {
self.close_modal();
}
response
}

fn modal_button(self, button: impl Into<egui::WidgetText>) -> egui::Response {
let response = self.button(button);
if response.clicked() {
self.close_modal();
}
response
}
}
Loading

0 comments on commit 11843a8

Please sign in to comment.