diff --git a/build.rs b/build.rs index 94db706..d294cbd 100644 --- a/build.rs +++ b/build.rs @@ -17,7 +17,10 @@ fn main() { compile_error!("Features `sdl2-static` and `sdl2-dynamic` are mutually exclusive!"); } } - let version = git_version!( args = ["--tags", "--always", "--dirty=-desync"], fallback = "v1" ); + let version = git_version!( + args = ["--tags", "--always", "--dirty=-desync"], + fallback = "v1" + ); rustc_env!("MFEK_VERSION", "{}", version); rustc_env!("MFEK_COMPILED_AT", "{}", chrono::Local::now().timestamp()); #[cfg(all(target_os = "macos", feature = "sdl2-dynamic"))] diff --git a/src/contour_operations/dashalongpath.rs b/src/contour_operations/dashalongpath.rs index f58e172..e27cfd1 100644 --- a/src/contour_operations/dashalongpath.rs +++ b/src/contour_operations/dashalongpath.rs @@ -1,15 +1,14 @@ -use MFEKmath::mfek::ResolveCubic; use glifparser::glif::contour::MFEKContourCommon; -use glifparser::glif::{MFEKContour, MFEKOutline, contour_operations::dash::DashContour}; -use glifparser::{MFEKPointData, Glif}; +use glifparser::glif::{contour_operations::dash::DashContour, MFEKContour, MFEKOutline}; +use glifparser::{Glif, MFEKPointData}; +use MFEKmath::mfek::ResolveCubic; use super::ContourOperationBuild; impl ContourOperationBuild for DashContour { fn build(&self, contour: &MFEKContour) -> MFEKOutline { - let mut glif = Glif::default(); - + // TODO: Get rid of this call to resolve to cubic and use some internal cache. glif.outline = Some(vec![contour.to_cubic().cubic().unwrap().clone()]); let dash_output = MFEKmath::dash_along_glif(&glif, self); diff --git a/src/contour_operations/mod.rs b/src/contour_operations/mod.rs index e5d27b2..a90f2f1 100644 --- a/src/contour_operations/mod.rs +++ b/src/contour_operations/mod.rs @@ -2,8 +2,8 @@ pub mod dashalongpath; pub mod patternalongpath; pub mod variablewidthstroke; +use glifparser::glif::contour_operations::{unknown_op_outline, ContourOperations}; use glifparser::glif::{MFEKContour, MFEKOutline}; -use glifparser::glif::contour_operations::{ContourOperations, unknown_op_outline}; use glifparser::MFEKPointData; pub trait ContourOperationBuild { diff --git a/src/contour_operations/patternalongpath.rs b/src/contour_operations/patternalongpath.rs index fbd0abf..3d1d739 100644 --- a/src/contour_operations/patternalongpath.rs +++ b/src/contour_operations/patternalongpath.rs @@ -1,8 +1,11 @@ -use glifparser::{glif::{MFEKContour, MFEKOutline, contour::MFEKContourCommon}, MFEKPointData}; use glifparser::glif::contour_operations::pap::PAPContour; +use glifparser::{ + glif::{contour::MFEKContourCommon, MFEKContour, MFEKOutline}, + MFEKPointData, +}; use MFEKmath::{pattern_along_path_mfek, Piecewise}; -use super::{ContourOperationBuild}; +use super::ContourOperationBuild; impl ContourOperationBuild for PAPContour { fn build(&self, contour: &MFEKContour) -> MFEKOutline { diff --git a/src/contour_operations/variablewidthstroke.rs b/src/contour_operations/variablewidthstroke.rs index c6825de..3dcf04c 100644 --- a/src/contour_operations/variablewidthstroke.rs +++ b/src/contour_operations/variablewidthstroke.rs @@ -1,7 +1,7 @@ -use MFEKmath::mfek::ResolveCubic; -use glifparser::MFEKPointData; +use glifparser::glif::contour_operations::vws::VWSContour; use glifparser::glif::{MFEKContour, MFEKOutline}; -use glifparser::glif::contour_operations::vws::{VWSContour}; +use glifparser::MFEKPointData; +use MFEKmath::mfek::ResolveCubic; use MFEKmath::{variable_width_stroke, Piecewise, VWSSettings}; use super::ContourOperationBuild; diff --git a/src/editor/contour_handlers/mod.rs b/src/editor/contour_handlers/mod.rs index 30fe0fb..20623e6 100644 --- a/src/editor/contour_handlers/mod.rs +++ b/src/editor/contour_handlers/mod.rs @@ -24,4 +24,4 @@ impl Editor { MFEKContourInnerType::Quad => Box::new(QuadHandler{}), } } -}*/ \ No newline at end of file +}*/ diff --git a/src/editor/io/mod.rs b/src/editor/io/mod.rs index c9fc25d..64a6171 100644 --- a/src/editor/io/mod.rs +++ b/src/editor/io/mod.rs @@ -1,7 +1,7 @@ use super::{events::*, Editor}; -use MFEKmath::mfek::ResolveCubic; use glifparser::glif::contour::MFEKContourCommon; +use MFEKmath::mfek::ResolveCubic; //use fs2::FileExt as _; # TODO: Add file locking. use glifparser::glif::mfek::{Layer, MFEKGlif}; use glifparser::{Glif, MFEKPointData}; @@ -134,14 +134,13 @@ impl Editor { if let Some(i) = interface { self.rebuild(i); } - + let mut export = self.prepare_export(); if export.layers.len() > 1 { log::warn!("In a flatten operation, layers not in the topmost group will be discarded and not in your chosen file. You may want to export (Ctrl+E) and not flatten."); } let layer = &mut export.layers[0]; - let glif_struct = self.glyph.as_ref().unwrap().to_exported(layer); let filename = self.with_glyph(|glyph| { @@ -370,9 +369,11 @@ pub trait ExportLayer { /// the normal MFEKGlif type's layers, then you will need to apply contour operations yourself! impl ExportLayer for MFEKGlif { fn to_exported(&self, layer: &mut Layer) -> Glif { - let contours: Vec<_> = layer.outline.iter_mut().map(|c| - c.to_cubic().cubic_mut().unwrap().clone() - ).collect(); + let contours: Vec<_> = layer + .outline + .iter_mut() + .map(|c| c.to_cubic().cubic_mut().unwrap().clone()) + .collect(); let mut ret = Glif::new(); ret.outline = Some(contours); ret.anchors = self.anchors.clone(); diff --git a/src/editor/macros.rs b/src/editor/macros.rs index 14fcd99..bdadda1 100644 --- a/src/editor/macros.rs +++ b/src/editor/macros.rs @@ -43,4 +43,6 @@ macro_rules! get_point_mut { // This re-import is here because I think it's messy to refer to these macros using the top-level // crate::. This allows me to have in modules e.g. `use crate::editor::macros::get_point`, which is // our preferred way of importing them. -pub use {get_contour, get_contour_len, get_contour_mut, is_contour_open, get_point, get_point_mut}; +pub use { + get_contour, get_contour_len, get_contour_mut, get_point, get_point_mut, is_contour_open, +}; diff --git a/src/editor/mod.rs b/src/editor/mod.rs index eb6cd37..ee02296 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -50,7 +50,7 @@ pub struct Editor { active_tool_enum: ToolEnum, clipboard: EditorClipboard, - layer_idx: Option, // active layer + layer_idx: Option, // active layer pub contour_idx: Option, // index into Outline pub point_idx: Option, @@ -115,7 +115,7 @@ impl Editor { /// This function MUST be called before calling with_active__mut or it will panic. /// Pushes a clone of the current layer onto the history stack and puts the editor in a modifying state. - /// When the fold argument is set to true the editor won't create new HistoryEntrys if the entry + /// When the fold argument is set to true the editor won't create new HistoryEntrys if the entry /// below has the same description. pub fn begin_modification(&mut self, description: &str, fold: bool) { log::trace!("Modification begun: {}", description); @@ -129,7 +129,6 @@ impl Editor { if fold && description.to_owned() == last_entry.description { return; } - } self.history.add_undo_entry(HistoryEntry { @@ -141,7 +140,6 @@ impl Editor { selected: Some(self.selected.clone()), glyph: self.glyph.as_ref().unwrap().clone(), }); - } /// When calling this family of functions the editor will become inaccessible because of the borrow on one of it's members. diff --git a/src/editor/operations.rs b/src/editor/operations.rs index 0073fc1..6a3259e 100644 --- a/src/editor/operations.rs +++ b/src/editor/operations.rs @@ -1,14 +1,14 @@ use crate::contour_operations::ContourOperationBuild; use crate::user_interface::Interface; -use MFEKmath::mfek::ResolveCubic; use glifparser::glif::contour::MFEKContourCommon; use glifparser::outline::skia::{FromSkiaPath, ToSkiaPaths}; -use glifparser::{FlattenedGlif, MFEKPointData}; use glifparser::{ glif::{Layer, LayerOperation}, MFEKGlif, Outline, }; +use glifparser::{FlattenedGlif, MFEKPointData}; use skia_safe::PathOp; +use MFEKmath::mfek::ResolveCubic; use super::Editor; @@ -74,7 +74,12 @@ impl Editor { .glyph .as_ref() .expect("Illegally tried to export a null glyph!"); - if glyph.layers.len() == 1 && glyph.layers[0].outline.iter().all(|c| c.operation().clone() == None) { + if glyph.layers.len() == 1 + && glyph.layers[0] + .outline + .iter() + .all(|c| c.operation().clone() == None) + { return glyph.clone(); } diff --git a/src/editor/selection.rs b/src/editor/selection.rs index b3947de..5aea603 100644 --- a/src/editor/selection.rs +++ b/src/editor/selection.rs @@ -1,10 +1,13 @@ use flo_curves::{bezier::fit_curve_cubic, BezierCurve}; use glifparser::{ - glif::{Layer, MFEKContour, contour::MFEKContourCommon, contour_operations::ContourOperation, inner::MFEKCommonInner}, + glif::{ + contour::MFEKContourCommon, contour_operations::ContourOperation, inner::MFEKCommonInner, + Layer, MFEKContour, + }, outline::skia::ToSkiaPaths as _, Handle, MFEKPointData, WhichHandle, }; -use MFEKmath::{Bezier, Evaluate, Vector, Rect}; +use MFEKmath::{Bezier, Evaluate, Rect, Vector}; use arboard::{self, Clipboard}; use serde_json; @@ -13,7 +16,6 @@ use shrinkwraprs; use super::Editor; use crate::user_interface::gui; - use std::collections::HashSet; use std::fmt; @@ -259,8 +261,7 @@ impl Editor { // if the contour is open and previous or next are out of bounds we're working with the start or end of the contour // so we abort and just delete the selection - if (point_idx == 0 || point_idx == contour.len() - 1) && contour.is_open() - { + if (point_idx == 0 || point_idx == contour.len() - 1) && contour.is_open() { self.delete_selection(); return; } @@ -375,7 +376,9 @@ impl Editor { contour[next_idx].b = Handle::At(fitted_curve.w3.x as f32, fitted_curve.w3.y as f32); contour.remove(point_idx); - layer.outline[contour_idx].operation_mut().remove_op(point_idx); + layer.outline[contour_idx] + .operation_mut() + .remove_op(point_idx); self.contour_idx = None; self.point_idx = None; @@ -450,19 +453,21 @@ impl Editor { pub fn build_selection_bounding_box(&self) -> Rect { let mut points = vec![]; for (ci, pi) in &self.selected { - let point = self.get_active_layer_ref().outline[*ci].get_point(*pi).unwrap(); + let point = self.get_active_layer_ref().outline[*ci] + .get_point(*pi) + .unwrap(); points.push(Vector { x: point.x() as f64, y: point.y() as f64, }); - + if let Some(Handle::At(x, y)) = point.get_handle(WhichHandle::A) { points.push(Vector { x: x as f64, y: y as f64, }); } - + if let Some(Handle::At(x, y)) = point.get_handle(WhichHandle::B) { points.push(Vector { x: x as f64, @@ -470,13 +475,13 @@ impl Editor { }); } } - + Rect::AABB_from_points(points) } - + pub fn get_selection_bounding_box_center(&self) -> (f32, f32) { let bounding_box = self.build_selection_bounding_box(); - + let half_width = ((bounding_box.left - bounding_box.right) / 2.) as f32; let half_height = ((bounding_box.top - bounding_box.bottom) / 2.) as f32; ( @@ -485,7 +490,6 @@ impl Editor { ) } - pub fn merge_contours(&mut self, start_contour: usize, end_contour: usize) { let (cidx, pidx) = { let layer = self.get_active_layer_mut(); diff --git a/src/editor/util.rs b/src/editor/util.rs index bed464f..6d66920 100644 --- a/src/editor/util.rs +++ b/src/editor/util.rs @@ -1,13 +1,13 @@ // This file is mainly utilities that are common use cases for the editor, but don't necessarily need to be // in Editor. -use crate::{get_contour_len, get_point_mut}; use crate::user_interface::Interface; +use crate::{get_contour_len, get_point_mut}; use flo_curves::{ bezier::{solve_curve_for_t_along_axis, Curve as FloCurve}, geo::Coord2, }; -use glifparser::{WhichHandle}; +use glifparser::WhichHandle; use glifrenderer::constants::{POINT_RADIUS, POINT_STROKE_THICKNESS}; use skia_safe::Contains; use skia_safe::Point as SkPoint; @@ -80,7 +80,10 @@ pub fn clicked_point_or_handle( let size = ((POINT_RADIUS * 2.) + (POINT_STROKE_THICKNESS * 2.)) * (1. / factor); // Topleft corner of point - let point_tl = SkPoint::new(point.x() as f32 - (size / 2.), point.y() as f32 - (size / 2.)); + let point_tl = SkPoint::new( + point.x() as f32 - (size / 2.), + point.y() as f32 - (size / 2.), + ); let point_rect = SkRect::from_point_and_size(point_tl, (size, size)); // winit::PhysicalPosition as an SkPoint @@ -93,9 +96,9 @@ pub fn clicked_point_or_handle( if let Some(handle_pos) = point.get_handle_position(WhichHandle::A) { let a_tl = SkPoint::new(handle_pos.0 - (size / 2.), handle_pos.1 - (size / 2.)); let a_rect = SkRect::from_point_and_size(a_tl, (size, size)); - + if a_rect.contains(sk_mpos) { - return Some((contour_idx, point_idx, WhichHandle::A)) + return Some((contour_idx, point_idx, WhichHandle::A)); } } @@ -104,7 +107,7 @@ pub fn clicked_point_or_handle( let b_rect = SkRect::from_point_and_size(b_tl, (size, size)); if b_rect.contains(sk_mpos) { - return Some((contour_idx, point_idx, WhichHandle::B)) + return Some((contour_idx, point_idx, WhichHandle::B)); } } } @@ -176,7 +179,7 @@ pub fn nearest_point_on_curve( &mouse_vec, 3.5 / i.viewport.factor as f64, ); - + if let Some(ct) = ct { use flo_curves::BezierCurve as _; use flo_curves::Coordinate as _; @@ -187,7 +190,7 @@ pub fn nearest_point_on_curve( t = Some(ct); contour_idx = Some(cx); seg_idx = Some(bx); - + let subdivisions = MathPrimitive::subdivide(mbezier, ct); if let Some(subdivisions) = subdivisions { h1 = Some(subdivisions.0.to_control_points()[2]); diff --git a/src/main.rs b/src/main.rs index d611d75..5dc32b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -338,7 +338,7 @@ fn main() { Event::MouseButtonDown { mouse_btn, x, y, .. - } => { + } => { let position = (x as f32, y as f32); let mouse_info = MouseInfo::new( &mut interface, @@ -349,10 +349,13 @@ fn main() { ); if mouse_btn == MouseButton::Middle { - editor.push_behavior(Box::new(PanBehavior::new(interface.viewport.clone(), mouse_info))); + editor.push_behavior(Box::new(PanBehavior::new( + interface.viewport.clone(), + mouse_info, + ))); continue; } - + editor.dispatch_editor_event( &mut interface, EditorEvent::MouseEvent { @@ -401,7 +404,7 @@ fn main() { interface.viewport.winsize = (x as f32, y as f32); interface.viewport.set_broken_flag(); interface.adjust_viewport_by_os_dpi(); - sk_surface = interface.create_surface( ); + sk_surface = interface.create_surface(); } _ => {} }, @@ -414,7 +417,7 @@ fn main() { &mut editor, &mut window_manager, &mut egui_manager, - &mut sk_surface + &mut sk_surface, ); } } diff --git a/src/tools/mod.rs b/src/tools/mod.rs index fd33976..676a1c3 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -3,9 +3,8 @@ pub_mod!("src/tools"); use self::prelude::*; use self::{ - anchors::Anchors, dash::Dash, guidelines::Guidelines, image::Image, - measure::Measure, pan::Pan, pap::PAP, pen::Pen, select::Select, shapes::Shapes, vws::VWS, - zoom::Zoom, + anchors::Anchors, dash::Dash, guidelines::Guidelines, image::Image, measure::Measure, pan::Pan, + pap::PAP, pen::Pen, select::Select, shapes::Shapes, vws::VWS, zoom::Zoom, }; use dyn_clone::DynClone; @@ -21,7 +20,9 @@ pub trait Tool: DynClone + std::fmt::Debug { fn draw(&mut self, _v: &Editor, _i: &Interface, _canvas: &mut Canvas) {} // UI hooks. Dialog hooks into the tools dialog. - fn dialog(&mut self, _v: &mut Editor, _i: &mut Interface, _ui: &mut Ui) -> bool { false } + fn dialog(&mut self, _v: &mut Editor, _i: &mut Interface, _ui: &mut Ui) -> bool { + false + } // TODO: Provide hooks for free floating UI (some tools might want this like pen for mode select) // and adding things to the right-click context menu. @@ -74,4 +75,4 @@ pub fn tool_enum_to_tool(tool: ToolEnum) -> Box { ToolEnum::PAP => Box::new(PAP::new()), ToolEnum::Guidelines => Box::new(Guidelines::new()), } -} \ No newline at end of file +} diff --git a/src/user_interface/egui_manager.rs b/src/user_interface/egui_manager.rs index 78fd6ca..ee38553 100644 --- a/src/user_interface/egui_manager.rs +++ b/src/user_interface/egui_manager.rs @@ -1,9 +1,9 @@ use egui_skia::EguiSkia; -use sdl2::{video::Window, event::Event}; +use sdl2::{event::Event, video::Window}; use super::Interface; -use egui_sdl2_event::{EguiSDL2State, DpiMode}; +use egui_sdl2_event::{DpiMode, EguiSDL2State}; pub struct EguiManager { pub egui: EguiSkia, @@ -12,7 +12,11 @@ pub struct EguiManager { impl EguiManager { pub fn new(interface: &mut Interface) -> Self { - let egui_sdl2 = egui_sdl2_event::EguiSDL2State::new(&interface.sdl_window, &interface.sdl_context.video().unwrap(), DpiMode::Auto); + let egui_sdl2 = egui_sdl2_event::EguiSDL2State::new( + &interface.sdl_window, + &interface.sdl_context.video().unwrap(), + DpiMode::Auto, + ); let egui_skia = egui_skia::EguiSkia::new(); return EguiManager { egui: egui_skia, @@ -25,23 +29,21 @@ impl EguiManager { // I don't think egui accepts all of these events, but it's a superset of the ones it does at least. match sdl_event { - Event::KeyDown{..} - | Event::KeyUp{..} - | Event::TextEditing{..} - | Event::TextInput{..} - => self.egui.egui_ctx.wants_keyboard_input(), - Event::MouseMotion{..} - | Event::MouseButtonDown{..} - | Event::MouseButtonUp{..} - | Event::MouseWheel{..} - | Event::FingerDown{..} - | Event::FingerUp{..} - | Event::FingerMotion{..} - | Event::DollarGesture{..} - | Event::DollarRecord{..} - | Event::MultiGesture{..} - => self.egui.egui_ctx.wants_pointer_input(), - _ => false, + Event::KeyDown { .. } + | Event::KeyUp { .. } + | Event::TextEditing { .. } + | Event::TextInput { .. } => self.egui.egui_ctx.wants_keyboard_input(), + Event::MouseMotion { .. } + | Event::MouseButtonDown { .. } + | Event::MouseButtonUp { .. } + | Event::MouseWheel { .. } + | Event::FingerDown { .. } + | Event::FingerUp { .. } + | Event::FingerMotion { .. } + | Event::DollarGesture { .. } + | Event::DollarRecord { .. } + | Event::MultiGesture { .. } => self.egui.egui_ctx.wants_pointer_input(), + _ => false, } } } diff --git a/src/user_interface/gui/menu_bar.rs b/src/user_interface/gui/menu_bar.rs index 0de2a1e..2f90756 100644 --- a/src/user_interface/gui/menu_bar.rs +++ b/src/user_interface/gui/menu_bar.rs @@ -1,7 +1,7 @@ use egui::Context; use glifrenderer::toggles::PreviewMode; -use crate::{filedialog, editor::Editor, user_interface::Interface}; +use crate::{editor::Editor, filedialog, user_interface::Interface}; use super::window::{GlifWindow, WindowManager}; @@ -11,11 +11,11 @@ pub fn menu_bar(ctx: &Context, v: &mut Editor, i: &mut Interface, wm: &mut Windo // // File // - ui.menu_button("File", |ui|{ + ui.menu_button("File", |ui| { if ui.button("Open").clicked() { match filedialog::open_filename(Some("glif,glifjson"), None) { Some(f) => v.load_glif(i, &f), - None => {}, + None => {} }; } if ui.button("Save").clicked() { @@ -67,7 +67,7 @@ pub fn menu_bar(ctx: &Context, v: &mut Editor, i: &mut Interface, wm: &mut Windo i.viewport.preview_mode = PreviewMode::Paper; } }); - + ui.checkbox(&mut i.grid.show, "Grid"); }); @@ -84,5 +84,5 @@ pub fn menu_bar(ctx: &Context, v: &mut Editor, i: &mut Interface, wm: &mut Windo wm.grid.set_open(grid_open); }) }) - }); -} \ No newline at end of file + }); +} diff --git a/src/user_interface/gui/mod.rs b/src/user_interface/gui/mod.rs index f7ce0dd..3167336 100644 --- a/src/user_interface/gui/mod.rs +++ b/src/user_interface/gui/mod.rs @@ -1,19 +1,18 @@ -pub mod prompts; pub mod menu_bar; +pub mod prompts; +pub mod textedit_buffer; pub mod tool_bar; pub mod window; pub mod windows; -pub mod textedit_buffer; #[macro_use] pub(crate) mod msgbox; -use std::cell::RefCell; -use crate::editor::Editor; -use super::{Interface, egui_manager::EguiManager}; -use self::{window::{GlifWindow, WindowManager}}; pub(crate) use self::msgbox::gui_error as error; - +use self::window::{GlifWindow, WindowManager}; +use super::{egui_manager::EguiManager, Interface}; +use crate::editor::Editor; +use std::cell::RefCell; pub fn build_ui( egui_manager: &mut EguiManager, @@ -36,7 +35,7 @@ pub fn build_ui( if i.active_prompts() { prompts::build_and_check_prompts(v, i, ctx); } - + v.dispatch_ui(i, ctx); }); } diff --git a/src/user_interface/gui/prompts.rs b/src/user_interface/gui/prompts.rs index e7d229e..5ebf830 100644 --- a/src/user_interface/gui/prompts.rs +++ b/src/user_interface/gui/prompts.rs @@ -10,14 +10,18 @@ use super::PROMPT_STR; pub fn build_and_check_prompts(v: &mut Editor, i: &mut Interface, ctx: &egui::Context) { match i.peek_prompt().clone() { - InputPrompt::YesNo { question, afterword, func } => { + InputPrompt::YesNo { + question, + afterword, + func, + } => { egui::Window::new("MFEKglif") .resizable(false) .collapsible(false) .anchor(Align2::CENTER_CENTER, [0., 0.]) - .fixed_pos( egui::Pos2::new( + .fixed_pos(egui::Pos2::new( (i.viewport.winsize.0 / 2.) * i.os_dpi(), - (i.viewport.winsize.1 / 2.) * i.os_dpi() + (i.viewport.winsize.1 / 2.) * i.os_dpi(), )) .show(ctx, |ui| { ui.label(format!("{}", question)); @@ -28,7 +32,7 @@ pub fn build_and_check_prompts(v: &mut Editor, i: &mut Interface, ctx: &egui::Co func(v, i, true); i.pop_prompt(); } - + if ui.button("No").clicked() { func(v, i, false); i.pop_prompt(); @@ -36,16 +40,19 @@ pub fn build_and_check_prompts(v: &mut Editor, i: &mut Interface, ctx: &egui::Co }); }); }); - - }, - InputPrompt::Text { label, default, func } => { + } + InputPrompt::Text { + label, + default, + func, + } => { egui::Window::new(label) .resizable(false) .collapsible(false) .anchor(Align2::CENTER_CENTER, [0., 0.]) - .fixed_pos( egui::Pos2::new( + .fixed_pos(egui::Pos2::new( (i.viewport.winsize.0 / 2.) * i.os_dpi(), - (i.viewport.winsize.1 / 2.) * i.os_dpi() + (i.viewport.winsize.1 / 2.) * i.os_dpi(), )) .show(ctx, |ui| { PROMPT_STR.with(|prompt_str| { @@ -59,6 +66,6 @@ pub fn build_and_check_prompts(v: &mut Editor, i: &mut Interface, ctx: &egui::Co prompt_str.replace(buffer); }) }); - }, + } } } diff --git a/src/user_interface/gui/textedit_buffer.rs b/src/user_interface/gui/textedit_buffer.rs index 26ab0d3..da083d3 100644 --- a/src/user_interface/gui/textedit_buffer.rs +++ b/src/user_interface/gui/textedit_buffer.rs @@ -24,4 +24,3 @@ impl EditBuffer for HashMap { self.insert(id.into(), v); } } - diff --git a/src/user_interface/gui/tool_bar.rs b/src/user_interface/gui/tool_bar.rs index 33365dd..c94e732 100644 --- a/src/user_interface/gui/tool_bar.rs +++ b/src/user_interface/gui/tool_bar.rs @@ -1,46 +1,53 @@ -use egui::{Align2, Context, Ui, Stroke, Color32}; +use egui::{Align2, Color32, Context, Stroke, Ui}; -use crate::{editor::Editor, user_interface::Interface, tools::ToolEnum}; +use crate::{editor::Editor, tools::ToolEnum, user_interface::Interface}; pub fn build_button(v: &mut Editor, ui: &mut Ui, text: &str, te: ToolEnum) { let stroke = if v.get_tool() == te { - Stroke{ width: 2.0, color: Color32::from_rgb(9, 82, 128),} - } else { Stroke::NONE }; + Stroke { + width: 2.0, + color: Color32::from_rgb(9, 82, 128), + } + } else { + Stroke::NONE + }; - let button = egui::Button::new(text).stroke(stroke).min_size(egui::vec2(22., 15.)); + let button = egui::Button::new(text) + .stroke(stroke) + .min_size(egui::vec2(22., 15.)); let respone = ui.add(button); if respone.clicked() { v.set_tool(te); } - + respone.on_hover_text(format!("{:?}", te)); } pub fn tool_bar(ctx: &Context, v: &mut Editor, _i: &mut Interface) { egui::Window::new("Tools") - .anchor(Align2::LEFT_TOP, [0., 25.]) - .title_bar(false) - .default_width(10.) - .min_width(15.) - .resizable(false) - .enabled(!v.is_modifying()) - .show(ctx, |ui| { - ui.vertical_centered(|ui| { - build_button(v, ui, "✋", ToolEnum::Pan); - build_button(v, ui, "⬈", ToolEnum::Select); - build_button(v, ui, "✒", ToolEnum::Pen); - ui.separator(); - build_button(v, ui, "🔎", ToolEnum::Zoom); - build_button(v, ui, "📏", ToolEnum::Measure); - ui.separator(); - build_button(v, ui, "⚡", ToolEnum::VWS); - build_button(v, ui, "〰", ToolEnum::PAP); - build_button(v, ui, "—", ToolEnum::Dash); - ui.separator(); - build_button(v, ui, "⚓", ToolEnum::Anchors); - build_button(v, ui, "⏺", ToolEnum::Shapes); - build_button(v, ui, "🖻", ToolEnum::Image); - build_button(v, ui, "|", ToolEnum::Guidelines); - }) - }); -} \ No newline at end of file + .anchor(Align2::LEFT_TOP, [0., 25.]) + .title_bar(false) + .default_width(10.) + .min_width(15.) + .resizable(false) + .enabled(!v.is_modifying()) + .show(ctx, |ui| { + ui.vertical_centered(|ui| { + build_button(v, ui, "✋", ToolEnum::Pan); + build_button(v, ui, "⬈", ToolEnum::Select); + build_button(v, ui, "✒", ToolEnum::Pen); + ui.separator(); + build_button(v, ui, "🔎", ToolEnum::Zoom); + build_button(v, ui, "📏", ToolEnum::Measure); + ui.separator(); + build_button(v, ui, "⚡", ToolEnum::VWS); + build_button(v, ui, "〰", ToolEnum::PAP); + build_button(v, ui, "—", ToolEnum::Dash); + ui.separator(); + build_button(v, ui, "⚓", ToolEnum::Anchors); + build_button(v, ui, "⏺", ToolEnum::Shapes); + build_button(v, ui, "🖻", ToolEnum::Image); + build_button(v, ui, "|", ToolEnum::Guidelines); + }) + }); +} diff --git a/src/user_interface/gui/window.rs b/src/user_interface/gui/window.rs index b144282..e6cc19a 100644 --- a/src/user_interface/gui/window.rs +++ b/src/user_interface/gui/window.rs @@ -1,7 +1,7 @@ use egui::Context; -use crate::{editor::Editor, user_interface::Interface}; use crate::user_interface::gui::windows::inspection_window::InspectionWindow; +use crate::{editor::Editor, user_interface::Interface}; use super::windows::grid_window::GridWindow; use super::windows::layer_list::LayerList; @@ -26,6 +26,6 @@ impl WindowManager { pub trait GlifWindow { fn open(&self) -> bool; fn set_open(&mut self, open: bool); - + fn build(&mut self, ctx: &Context, v: &mut Editor, i: &mut Interface); -} \ No newline at end of file +} diff --git a/src/user_interface/gui/windows/grid_window.rs b/src/user_interface/gui/windows/grid_window.rs index 7452cda..b5c9b48 100644 --- a/src/user_interface/gui/windows/grid_window.rs +++ b/src/user_interface/gui/windows/grid_window.rs @@ -1,20 +1,23 @@ use std::collections::HashMap; -use egui::{Context}; -use crate::{editor::Editor, user_interface::{Interface, gui::window::GlifWindow}}; use super::egui_parsed_textfield; +use crate::{ + editor::Editor, + user_interface::{gui::window::GlifWindow, Interface}, +}; +use egui::Context; pub struct GridWindow { // is this window open? open: bool, - edit_buf: HashMap + edit_buf: HashMap, } impl GridWindow { pub fn new() -> Self { - Self { + Self { open: false, - edit_buf: HashMap::new() + edit_buf: HashMap::new(), } } } @@ -30,25 +33,27 @@ impl GlifWindow for GridWindow { fn build(&mut self, ctx: &Context, v: &mut Editor, i: &mut Interface) { egui::Window::new("Grid") - .resizable(true) - .collapsible(true) - .open(&mut self.open) - .enabled(!v.is_modifying()) - .constrain(true) - .default_width(100.) - .show(ctx, |ui| { + .resizable(true) + .collapsible(true) + .open(&mut self.open) + .enabled(!v.is_modifying()) + .constrain(true) + .default_width(100.) + .show(ctx, |ui| { ui.checkbox(&mut i.grid.show, "Active"); ui.separator(); ui.horizontal(|ui| { ui.label("Spacing"); - i.grid.spacing = egui_parsed_textfield(ui, "spacing", i.grid.spacing, &mut self.edit_buf); + i.grid.spacing = + egui_parsed_textfield(ui, "spacing", i.grid.spacing, &mut self.edit_buf); }); ui.horizontal(|ui| { ui.label("Offset"); - i.grid.offset = egui_parsed_textfield(ui, "offset", i.grid.spacing, &mut self.edit_buf); + i.grid.offset = + egui_parsed_textfield(ui, "offset", i.grid.spacing, &mut self.edit_buf); }); let prev_italic = i.grid.slope.is_some(); @@ -62,13 +67,18 @@ impl GlifWindow for GridWindow { } if let Some(slope) = i.grid.slope { - i.grid.slope = Some(egui_parsed_textfield(ui, "slope", slope, &mut self.edit_buf)); + i.grid.slope = Some(egui_parsed_textfield( + ui, + "slope", + slope, + &mut self.edit_buf, + )); let mut angle = (f32::to_degrees(f32::atan(slope)) * 10000.).round() / 10000.; angle = egui_parsed_textfield(ui, "spacing", angle, &mut self.edit_buf); } i.grid.offset %= i.grid.spacing; - }); + }); } } diff --git a/src/user_interface/gui/windows/inspection_window.rs b/src/user_interface/gui/windows/inspection_window.rs index 478b6b0..8d1f11d 100644 --- a/src/user_interface/gui/windows/inspection_window.rs +++ b/src/user_interface/gui/windows/inspection_window.rs @@ -1,8 +1,14 @@ use std::collections::HashMap; -use egui::{Context}; -use crate::{editor::Editor, user_interface::{Interface, gui::window::GlifWindow}}; -use glifparser::{glif::{contour::MFEKContourCommon, point::MFEKPointCommon}, PointData, WhichHandle, Handle}; +use crate::{ + editor::Editor, + user_interface::{gui::window::GlifWindow, Interface}, +}; +use egui::Context; +use glifparser::{ + glif::{contour::MFEKContourCommon, point::MFEKPointCommon}, + Handle, PointData, WhichHandle, +}; use super::egui_parsed_textfield; @@ -14,9 +20,9 @@ pub struct InspectionWindow { impl InspectionWindow { pub fn new() -> Self { - InspectionWindow { + InspectionWindow { open: false, - edit_buf: HashMap::new() + edit_buf: HashMap::new(), } } } @@ -42,19 +48,20 @@ impl GlifWindow for InspectionWindow { .show(ctx, |ui| { if let Some(selected_point) = v.selected_point() { let mut contour = v.get_active_layer_ref().outline[selected_point.0].clone(); - let point = contour.get_point_mut(selected_point.1).expect("Editor should have valid selection!"); + let point = contour + .get_point_mut(selected_point.1) + .expect("Editor should have valid selection!"); // do contour stuff ui.collapsing("Contour", |ui| { - let mut contour = v.get_active_layer_ref().outline[selected_point.0].clone(); + let mut contour = + v.get_active_layer_ref().outline[selected_point.0].clone(); ui.label(format!("Type: {:?}", contour.get_type())); - + if let Some(op) = contour.operation() { ui.label(format!("Operation: {:?}", op)); - if ui.button("Apply Contour Operation").clicked() { - - } + if ui.button("Apply Contour Operation").clicked() {} } let mut open = contour.is_open(); @@ -78,52 +85,82 @@ impl GlifWindow for InspectionWindow { egui_parsed_textfield(ui, "px", point.x(), &mut self.edit_buf), egui_parsed_textfield(ui, "py", point.y(), &mut self.edit_buf), ); - + ui.collapsing("Handles", |ui| { if point.has_handle(WhichHandle::A) { ui.label("Handle A:"); - if let Some(handle_a_pos) = point.get_handle_position(WhichHandle::A) { - point.set_handle_position(WhichHandle::A, - egui_parsed_textfield(ui, "hax", handle_a_pos.0, &mut self.edit_buf), - egui_parsed_textfield(ui, "hay", handle_a_pos.1, &mut self.edit_buf), + if let Some(handle_a_pos) = + point.get_handle_position(WhichHandle::A) + { + point.set_handle_position( + WhichHandle::A, + egui_parsed_textfield( + ui, + "hax", + handle_a_pos.0, + &mut self.edit_buf, + ), + egui_parsed_textfield( + ui, + "hay", + handle_a_pos.1, + &mut self.edit_buf, + ), ) } - - let prev_checked = point.get_handle(WhichHandle::A).unwrap() == Handle::Colocated; - let mut checked = point.get_handle(WhichHandle::A).unwrap() == Handle::Colocated; + + let prev_checked = + point.get_handle(WhichHandle::A).unwrap() == Handle::Colocated; + let mut checked = + point.get_handle(WhichHandle::A).unwrap() == Handle::Colocated; ui.checkbox(&mut checked, "Colocated A"); - + if prev_checked != checked { match checked { false => { let (x, y) = point.get_position(); point.set_handle_position(WhichHandle::A, x, y); - }, + } true => { point.colocate_handle(WhichHandle::A); } } } } - + if point.has_handle(WhichHandle::B) { ui.label("Handle B:"); - if let Some(handle_b_pos) = point.get_handle_position(WhichHandle::B) { - point.set_handle_position(WhichHandle::B, - egui_parsed_textfield(ui, "hbx", handle_b_pos.0, &mut self.edit_buf), - egui_parsed_textfield(ui, "hby", handle_b_pos.1, &mut self.edit_buf) + if let Some(handle_b_pos) = + point.get_handle_position(WhichHandle::B) + { + point.set_handle_position( + WhichHandle::B, + egui_parsed_textfield( + ui, + "hbx", + handle_b_pos.0, + &mut self.edit_buf, + ), + egui_parsed_textfield( + ui, + "hby", + handle_b_pos.1, + &mut self.edit_buf, + ), ) } - let prev_checked = point.get_handle(WhichHandle::B).unwrap() == Handle::Colocated; - let mut checked = point.get_handle(WhichHandle::B).unwrap() == Handle::Colocated; + let prev_checked = + point.get_handle(WhichHandle::B).unwrap() == Handle::Colocated; + let mut checked = + point.get_handle(WhichHandle::B).unwrap() == Handle::Colocated; ui.checkbox(&mut checked, "Colocated B"); - + if prev_checked != checked { match checked { false => { let (x, y) = point.get_position(); point.set_handle_position(WhichHandle::B, x, y); - }, + } true => { point.colocate_handle(WhichHandle::B); } @@ -131,27 +168,33 @@ impl GlifWindow for InspectionWindow { } } }); - - - if !point_equivalent(point, v.get_active_layer_ref().outline[selected_point.0].get_point(selected_point.1).unwrap()) { + + if !point_equivalent( + point, + v.get_active_layer_ref().outline[selected_point.0] + .get_point(selected_point.1) + .unwrap(), + ) { v.begin_modification("Modified point with inspector.", true); - - let mutated_point = v.get_active_layer_mut().outline[selected_point.0].get_point_mut(selected_point.1).unwrap(); + + let mutated_point = v.get_active_layer_mut().outline[selected_point.0] + .get_point_mut(selected_point.1) + .unwrap(); let (x, y) = point.get_position(); mutated_point.set_position(x, y); - + if let Some(handle_a) = point.get_handle(WhichHandle::A) { mutated_point.set_handle(WhichHandle::A, handle_a); } - + if let Some(handle_b) = point.get_handle(WhichHandle::B) { mutated_point.set_handle(WhichHandle::B, handle_b); } - + if let Some(name) = point.get_name() { mutated_point.set_name(name); } - + v.end_modification(); } }); @@ -162,201 +205,204 @@ impl GlifWindow for InspectionWindow { } } -fn point_equivalent(a: &dyn MFEKPointCommon, b: &dyn MFEKPointCommon) -> bool { - a.get_name() == b.get_name() && - a.get_position() == b.get_position() && - a.get_handle_position(WhichHandle::A) == b.get_handle_position(WhichHandle::A) && - a.get_handle_position(WhichHandle::B) == b.get_handle_position(WhichHandle::B) +fn point_equivalent( + a: &dyn MFEKPointCommon, + b: &dyn MFEKPointCommon, +) -> bool { + a.get_name() == b.get_name() + && a.get_position() == b.get_position() + && a.get_handle_position(WhichHandle::A) == b.get_handle_position(WhichHandle::A) + && a.get_handle_position(WhichHandle::B) == b.get_handle_position(WhichHandle::B) } - /* - let (ci, pi) = if let Some((ci, pi)) = v.selected_point() { - (ci, pi) - } else { - return; - }; +/* +let (ci, pi) = if let Some((ci, pi)) = v.selected_point() { + (ci, pi) +} else { + return; +}; + +let layer = v.get_active_layer_ref(); +if v.get_active_layer_ref().outline[ci].get_type() != MFEKContourInnerType::Cubic { + return +} +let point = get_point!(layer, ci, pi).unwrap().cubic().unwrap().clone(); + +let multiple_points_selected = v.selected.len() > 1; + +let (tx, ty, tw, th) = i.get_tools_dialog_rect(); +let mut should_clear_contour_op = false; +let mut should_apply_contour_op = false; +let on_open_contour = is_contour_open!(v.get_active_layer_ref(), ci); +let contour_len = get_contour_len!(v.get_active_layer_ref(), ci); +let on_last_open_point: bool = pi == contour_len - 1 && on_open_contour; +let on_first_open_point: bool = pi == 0 && on_open_contour; + +let mut new_point: Point = point.clone(); +let mut pname = imgui::ImString::from( + new_point + .name + .as_ref() + .map(|n| n.to_string()) + .unwrap_or_else(String::new), +); +pname.reserve(IMGUI_RESERVE); + +imgui::Window::new(&if multiple_points_selected { + imgui::ImString::new("Points") +} else { + imgui::im_str!("Point @({}, {}) of type {:?}", ci, pi, new_point.ptype) +}) +.bg_alpha(1.) // See comment on fn redraw_skia +.flags( + imgui::WindowFlags::NO_RESIZE + | imgui::WindowFlags::NO_MOVE + | imgui::WindowFlags::NO_COLLAPSE, +) +.position( + [tx, ty - DIALOG_ADDITIONAL_HEIGHT], + imgui::Condition::Always, +) +.size( + [tw, th + DIALOG_ADDITIONAL_HEIGHT], + imgui::Condition::Always, +) +.build(ui, || { + if multiple_points_selected { + ui.text(imgui::im_str!("Multiple points selected")); + return; + } + + // X + imgui_decimal_text_field("X", ui, &mut new_point.x, None); + // Y + imgui_decimal_text_field("Y", ui, &mut new_point.y, None); - let layer = v.get_active_layer_ref(); - if v.get_active_layer_ref().outline[ci].get_type() != MFEKContourInnerType::Cubic { - return + let mut a_colocated = new_point.a == Handle::Colocated; + let mut b_colocated = new_point.b == Handle::Colocated; + // A (next) + if !on_last_open_point { + ui.text(imgui::im_str!("Next off-curve point")); + ui.checkbox(imgui::im_str!("A Colocated"), &mut a_colocated); + // AX + let (mut ax, mut ay) = new_point.handle_or_colocated(WhichHandle::A, &|f| f, &|f| f); + let orig_axy = (ax, ay); + imgui_decimal_text_field("AX", ui, &mut ax, None); + // AY + imgui_decimal_text_field("AY", ui, &mut ay, None); + + if (ax, ay) != orig_axy { + new_point.a = Handle::At(ax, ay); + new_point.ptype = PointType::Curve; + } else if a_colocated { + new_point.a = Handle::Colocated; } - let point = get_point!(layer, ci, pi).unwrap().cubic().unwrap().clone(); - - let multiple_points_selected = v.selected.len() > 1; - - let (tx, ty, tw, th) = i.get_tools_dialog_rect(); - let mut should_clear_contour_op = false; - let mut should_apply_contour_op = false; - let on_open_contour = is_contour_open!(v.get_active_layer_ref(), ci); - let contour_len = get_contour_len!(v.get_active_layer_ref(), ci); - let on_last_open_point: bool = pi == contour_len - 1 && on_open_contour; - let on_first_open_point: bool = pi == 0 && on_open_contour; - - let mut new_point: Point = point.clone(); - let mut pname = imgui::ImString::from( - new_point - .name - .as_ref() - .map(|n| n.to_string()) - .unwrap_or_else(String::new), - ); - pname.reserve(IMGUI_RESERVE); - - imgui::Window::new(&if multiple_points_selected { - imgui::ImString::new("Points") - } else { - imgui::im_str!("Point @({}, {}) of type {:?}", ci, pi, new_point.ptype) - }) - .bg_alpha(1.) // See comment on fn redraw_skia - .flags( - imgui::WindowFlags::NO_RESIZE - | imgui::WindowFlags::NO_MOVE - | imgui::WindowFlags::NO_COLLAPSE, - ) - .position( - [tx, ty - DIALOG_ADDITIONAL_HEIGHT], - imgui::Condition::Always, - ) - .size( - [tw, th + DIALOG_ADDITIONAL_HEIGHT], - imgui::Condition::Always, - ) - .build(ui, || { - if multiple_points_selected { - ui.text(imgui::im_str!("Multiple points selected")); - return; - } - - // X - imgui_decimal_text_field("X", ui, &mut new_point.x, None); - // Y - imgui_decimal_text_field("Y", ui, &mut new_point.y, None); - - let mut a_colocated = new_point.a == Handle::Colocated; - let mut b_colocated = new_point.b == Handle::Colocated; - // A (next) - if !on_last_open_point { - ui.text(imgui::im_str!("Next off-curve point")); - ui.checkbox(imgui::im_str!("A Colocated"), &mut a_colocated); - // AX - let (mut ax, mut ay) = new_point.handle_or_colocated(WhichHandle::A, &|f| f, &|f| f); - let orig_axy = (ax, ay); - imgui_decimal_text_field("AX", ui, &mut ax, None); - // AY - imgui_decimal_text_field("AY", ui, &mut ay, None); - - if (ax, ay) != orig_axy { - new_point.a = Handle::At(ax, ay); - new_point.ptype = PointType::Curve; - } else if a_colocated { - new_point.a = Handle::Colocated; - } - // Ar, AΘ - imgui_radius_theta("A", ui, WhichHandle::A, &mut new_point); - } - - // B (prev) - if !on_first_open_point { - ui.text(imgui::im_str!("Previous off-curve point")); - ui.checkbox(imgui::im_str!("B Colocated"), &mut b_colocated); - // BX - let (mut bx, mut by) = new_point.handle_or_colocated(WhichHandle::B, &|f| f, &|f| f); - let orig_bxy = (bx, by); - imgui_decimal_text_field("BX", ui, &mut bx, None); - // BY - imgui_decimal_text_field("BY", ui, &mut by, None); - if (bx, by) != orig_bxy { - new_point.b = Handle::At(bx, by); - new_point.ptype = PointType::Curve; - } else if b_colocated { - new_point.b = Handle::Colocated; - } - // Br, BΘ - imgui_radius_theta("B", ui, WhichHandle::B, &mut new_point); - } - - let name_field = ui - .input_text(imgui::im_str!("Name"), &mut pname) - .enter_returns_true(true); - if name_field.build() { - if pname.to_str().len() > 0 { - new_point.name = Some(pname.to_string()); - } else { - new_point.name = None; - } - } + // Ar, AΘ + imgui_radius_theta("A", ui, WhichHandle::A, &mut new_point); + } - if v.get_active_layer_ref().outline[ci].operation().is_some() { - ui.button(imgui::im_str!("Reset Contour Operation"), [0., 0.]); - if ui.is_item_clicked(imgui::MouseButton::Left) { - should_clear_contour_op = true; - } - ui.button(imgui::im_str!("Apply Contour Operation"), [0., 0.]); - if ui.is_item_clicked(imgui::MouseButton::Left) { - should_apply_contour_op = true; - } - } - }); + // B (prev) + if !on_first_open_point { + ui.text(imgui::im_str!("Previous off-curve point")); + ui.checkbox(imgui::im_str!("B Colocated"), &mut b_colocated); + // BX + let (mut bx, mut by) = new_point.handle_or_colocated(WhichHandle::B, &|f| f, &|f| f); + let orig_bxy = (bx, by); + imgui_decimal_text_field("BX", ui, &mut bx, None); + // BY + imgui_decimal_text_field("BY", ui, &mut by, None); + if (bx, by) != orig_bxy { + new_point.b = Handle::At(bx, by); + new_point.ptype = PointType::Curve; + } else if b_colocated { + new_point.b = Handle::Colocated; + } + // Br, BΘ + imgui_radius_theta("B", ui, WhichHandle::B, &mut new_point); + } - if point.ptype == PointType::Move { - new_point.ptype = PointType::Move; + let name_field = ui + .input_text(imgui::im_str!("Name"), &mut pname) + .enter_returns_true(true); + if name_field.build() { + if pname.to_str().len() > 0 { + new_point.name = Some(pname.to_string()); + } else { + new_point.name = None; } + } - if should_clear_contour_op { - v.begin_modification("Reset contour op."); - v.get_active_layer_mut().outline[ci].set_operation(None); - v.end_modification(); + if v.get_active_layer_ref().outline[ci].operation().is_some() { + ui.button(imgui::im_str!("Reset Contour Operation"), [0., 0.]); + if ui.is_item_clicked(imgui::MouseButton::Left) { + should_clear_contour_op = true; + } + ui.button(imgui::im_str!("Apply Contour Operation"), [0., 0.]); + if ui.is_item_clicked(imgui::MouseButton::Left) { + should_apply_contour_op = true; } + } +}); - if should_apply_contour_op { - v.begin_modification("Apply contour op."); - { - let layer = v.get_active_layer_mut(); - let op = &layer.outline[ci].operation().clone(); - layer.outline[ci].set_operation(None); - let ol = op.build(&layer.outline[ci]); - layer.outline.remove(ci); - for contour in ol { - layer.outline.push(contour); - } - }; - v.contour_idx = None; - v.point_idx = None; - v.selected = HashSet::new(); - v.end_modification(); +if point.ptype == PointType::Move { + new_point.ptype = PointType::Move; +} + +if should_clear_contour_op { + v.begin_modification("Reset contour op."); + v.get_active_layer_mut().outline[ci].set_operation(None); + v.end_modification(); +} + +if should_apply_contour_op { + v.begin_modification("Apply contour op."); + { + let layer = v.get_active_layer_mut(); + let op = &layer.outline[ci].operation().clone(); + layer.outline[ci].set_operation(None); + let ol = op.build(&layer.outline[ci]); + layer.outline.remove(ci); + for contour in ol { + layer.outline.push(contour); } + }; + v.contour_idx = None; + v.point_idx = None; + v.selected = HashSet::new(); + v.end_modification(); +} - if point.x != new_point.x - || point.y != new_point.y - || point.a != new_point.a - || point.b != new_point.b - || point.name != new_point.name - || point.ptype != new_point.ptype - { - v.begin_modification("Point properties changed (dialog)"); - { - let layer = v.get_active_layer_mut(); - match &mut layer.outline[ci].inner_mut() { - MFEKContourInner::Cubic(contour) => contour[pi] = new_point, - _ => panic!("Unsupported") - } - } - v.end_modification(); +if point.x != new_point.x + || point.y != new_point.y + || point.a != new_point.a + || point.b != new_point.b + || point.name != new_point.name + || point.ptype != new_point.ptype +{ + v.begin_modification("Point properties changed (dialog)"); + { + let layer = v.get_active_layer_mut(); + match &mut layer.outline[ci].inner_mut() { + MFEKContourInner::Cubic(contour) => contour[pi] = new_point, + _ => panic!("Unsupported") } + } + v.end_modification(); +} - // unsafe function! OK here as these handles are always invalid and if we used history - // version then it would be an invalid begin_modification() (from dialog) inside a - // begin_modification() (from moving handles). - v.with_active_layer_mut_no_history(|layer| { - if on_first_open_point { - match &mut layer.outline[ci].inner_mut() { - MFEKContourInner::Cubic(contour) => contour[pi].b = Handle::Colocated, - _ => unreachable!() - } - } else if on_last_open_point { - match &mut layer.outline[ci].inner_mut() { - MFEKContourInner::Cubic(contour) => contour[pi].a = Handle::Colocated, - _ => unreachable!() - } } - }); - */ \ No newline at end of file +// unsafe function! OK here as these handles are always invalid and if we used history +// version then it would be an invalid begin_modification() (from dialog) inside a +// begin_modification() (from moving handles). +v.with_active_layer_mut_no_history(|layer| { + if on_first_open_point { + match &mut layer.outline[ci].inner_mut() { + MFEKContourInner::Cubic(contour) => contour[pi].b = Handle::Colocated, + _ => unreachable!() + } + } else if on_last_open_point { + match &mut layer.outline[ci].inner_mut() { + MFEKContourInner::Cubic(contour) => contour[pi].a = Handle::Colocated, + _ => unreachable!() + } } +}); +*/ diff --git a/src/user_interface/gui/windows/layer_list.rs b/src/user_interface/gui/windows/layer_list.rs index 78945a9..3f089b0 100644 --- a/src/user_interface/gui/windows/layer_list.rs +++ b/src/user_interface/gui/windows/layer_list.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; -use egui::{Context, Align2}; -use glifparser::Color; +use egui::{Align2, Context}; use glifparser::glif::LayerOperation; +use glifparser::Color; use crate::editor::Editor; use crate::user_interface::gui::textedit_buffer::EditBuffer; @@ -12,7 +12,7 @@ use crate::Interface; pub struct LayerList { popup_had_focus: bool, cur_popup: Option, - edit_buf: HashMap + edit_buf: HashMap, } impl LayerList { @@ -20,99 +20,100 @@ impl LayerList { Self { popup_had_focus: false, cur_popup: None, - edit_buf: HashMap::new() + edit_buf: HashMap::new(), } } pub fn build(&mut self, ctx: &Context, v: &mut Editor, _i: &mut Interface) { let active_layer = v.get_active_layer(); - + egui::Window::new("Layers") .resizable(true) .vscroll(true) .default_width(200.) .enabled(!v.is_modifying()) .anchor(Align2::RIGHT_BOTTOM, [0., 0.]) - .resize(|r| { - r.default_width(200.) - }) + .resize(|r| r.default_width(200.)) .show(ctx, |ui| { let mut selected_layer = v.get_active_layer(); ui.horizontal(|ui| { if ui.button("➕").clicked() { v.new_layer(); } - + if ui.button("➖").clicked() { v.delete_layer(); } - + if ui.button("⮫").clicked() { v.swap_layers(active_layer, active_layer - 1, true); } - - + if ui.button("⮨").clicked() { v.swap_layers(active_layer, active_layer + 1, true); } - }); - + }); + ui.separator(); - + let layer_count = v.get_layer_count(); - for layer in 0..layer_count { - - ui.horizontal(|ui| { - // Show/hide button eye is visible shades is hidden - let eye_con = if v.with_glyph(|glif| glif.layers[layer].visible) { - "👁" - } else { - "👓" - }; - - let eye_button = egui::Button::new(eye_con) - .min_size(egui::vec2(24., 0.)); - - - if ui.add(eye_button).on_hover_text("Visible").clicked() { - let active_layer = v.get_active_layer(); - v.set_active_layer(layer); - - v.begin_modification("Toggled layer visibility.", false); - v.get_active_layer_mut().visible = !v.get_active_layer_ref().visible; - v.end_modification(); - - v.set_active_layer(active_layer); - } - - let response = ui.button("📛").on_hover_text("Rename"); + for layer in 0..layer_count { + ui.horizontal(|ui| { + // Show/hide button eye is visible shades is hidden + let eye_con = if v.with_glyph(|glif| glif.layers[layer].visible) { + "👁" + } else { + "👓" + }; - let popup_id = ui.make_persistent_id(format!("layer{0}namepopup", layer )); - let name_clicked = response.clicked(); - if response.clicked() { - self.cur_popup = Some(format!("layer{0}namepopup", layer ).to_string()); - } + let eye_button = egui::Button::new(eye_con).min_size(egui::vec2(24., 0.)); - if self.cur_popup == Some(format!("layer{0}namepopup", layer ).to_string()) { - ui.memory_mut(|m|m.open_popup(popup_id)); - } - - egui::popup::popup_above_or_below_widget(ui, popup_id, &response, egui::AboveOrBelow::Above, |ui| { + if ui.add(eye_button).on_hover_text("Visible").clicked() { + let active_layer = v.get_active_layer(); + v.set_active_layer(layer); + + v.begin_modification("Toggled layer visibility.", false); + v.get_active_layer_mut().visible = !v.get_active_layer_ref().visible; + v.end_modification(); + + v.set_active_layer(active_layer); + } + + let response = ui.button("📛").on_hover_text("Rename"); + + let popup_id = ui.make_persistent_id(format!("layer{0}namepopup", layer)); + let name_clicked = response.clicked(); + if response.clicked() { + self.cur_popup = Some(format!("layer{0}namepopup", layer).to_string()); + } + + if self.cur_popup == Some(format!("layer{0}namepopup", layer).to_string()) { + ui.memory_mut(|m| m.open_popup(popup_id)); + } + + egui::popup::popup_above_or_below_widget( + ui, + popup_id, + &response, + egui::AboveOrBelow::Above, + |ui| { ui.set_min_width(64.); let name = v.with_glyph(|glif| glif.layers[layer].name.clone()); - let edit_response = ui.text_edit_singleline(self.edit_buf.get_buf("name", &name)); + let edit_response = + ui.text_edit_singleline(self.edit_buf.get_buf("name", &name)); if edit_response.lost_focus() && self.popup_had_focus { if self.edit_buf.get_buf("name", &name).clone() != name { let active_layer = v.get_active_layer(); v.set_active_layer(layer); v.begin_modification("Changed layer name.", true); - v.get_active_layer_mut().name = self.edit_buf.get_buf("name", &name).clone(); + v.get_active_layer_mut().name = + self.edit_buf.get_buf("name", &name).clone(); v.end_modification(); v.set_active_layer(active_layer) } self.popup_had_focus = false; self.cur_popup = None; - ui.memory_mut(|m|m.close_popup()); + ui.memory_mut(|m| m.close_popup()); } if !edit_response.has_focus() { @@ -124,20 +125,27 @@ impl LayerList { if edit_response.clicked_elsewhere() && !name_clicked { self.popup_had_focus = false; self.cur_popup = None; - ui.memory_mut(|m|m.close_popup()); + ui.memory_mut(|m| m.close_popup()); } - }); - - let popup_id = ui.make_persistent_id(format!("layer{0}popup", layer )); - let current_operation = v.with_glyph(|glif| glif.layers[layer].operation.clone()); - let mut selected_operation = current_operation.clone(); - - let response = ui.button("💇").on_hover_text("Layer Operation"); - if response.clicked() { - ui.memory_mut(|m|m.toggle_popup(popup_id)); - } - - egui::popup::popup_above_or_below_widget(ui, popup_id, &response, egui::AboveOrBelow::Above, |ui| { + }, + ); + + let popup_id = ui.make_persistent_id(format!("layer{0}popup", layer)); + let current_operation = + v.with_glyph(|glif| glif.layers[layer].operation.clone()); + let mut selected_operation = current_operation.clone(); + + let response = ui.button("💇").on_hover_text("Layer Operation"); + if response.clicked() { + ui.memory_mut(|m| m.toggle_popup(popup_id)); + } + + egui::popup::popup_above_or_below_widget( + ui, + popup_id, + &response, + egui::AboveOrBelow::Above, + |ui| { ui.set_min_width(96.); if ui.button("None").clicked() { selected_operation = None; @@ -154,47 +162,47 @@ impl LayerList { if ui.button("Intersect").clicked() { selected_operation = Some(LayerOperation::Intersect); } - }); - - if selected_operation != current_operation{ + }, + ); + + if selected_operation != current_operation { + let active_layer = v.get_active_layer(); + v.set_active_layer(layer); + v.begin_modification("Changed layer operation.", false); + v.get_active_layer_mut().operation = selected_operation; + v.end_modification(); + v.set_active_layer(active_layer); + } + + if current_operation.is_none() { + let cur_color = v.with_glyph(|g| g.layers[layer].color); + + let mut color_array: [f32; 4] = + cur_color.unwrap_or(Color::default()).into(); + let orig_color = color_array.clone(); + ui.set_min_width(24.); + ui.color_edit_button_rgba_unmultiplied(&mut color_array); + + if color_array != orig_color { let active_layer = v.get_active_layer(); v.set_active_layer(layer); - v.begin_modification("Changed layer operation.", false); - v.get_active_layer_mut().operation = selected_operation; + + v.begin_modification("Changed layer color.", true); + v.get_active_layer_mut().color = Some(color_array.into()); v.end_modification(); + v.set_active_layer(active_layer); } - - if current_operation.is_none() { - let cur_color = v.with_glyph(|g| g.layers[layer].color); - - let mut color_array: [f32;4] = cur_color.unwrap_or(Color::default()).into(); - let orig_color = color_array.clone(); - ui.set_min_width(24.); - ui.color_edit_button_rgba_unmultiplied(&mut color_array); - - if color_array != orig_color { - let active_layer = v.get_active_layer(); - v.set_active_layer(layer); - - v.begin_modification("Changed layer color.", true); - v.get_active_layer_mut().color = Some(color_array.into()); - v.end_modification(); - - v.set_active_layer(active_layer); - } - } - - let layer_name = v.with_glyph(|g| g.layers[layer].name.clone()); - ui.selectable_value(&mut selected_layer, layer, layer_name); - }); - } - - + } + + let layer_name = v.with_glyph(|g| g.layers[layer].name.clone()); + ui.selectable_value(&mut selected_layer, layer, layer_name); + }); + } + if selected_layer != v.get_active_layer() { v.set_active_layer(selected_layer); } - }); } } diff --git a/src/user_interface/gui/windows/mod.rs b/src/user_interface/gui/windows/mod.rs index 06af57c..39e45c1 100644 --- a/src/user_interface/gui/windows/mod.rs +++ b/src/user_interface/gui/windows/mod.rs @@ -4,13 +4,20 @@ use egui::Ui; use super::textedit_buffer::EditBuffer; -pub mod inspection_window; pub mod grid_window; -pub mod tool_window; +pub mod inspection_window; pub mod layer_list; +pub mod tool_window; -pub fn egui_parsed_textfield(ui: &mut Ui, id: impl Into, default: D, editbuf: &mut impl EditBuffer) -> D - where D: FromStr + ToString { +pub fn egui_parsed_textfield( + ui: &mut Ui, + id: impl Into, + default: D, + editbuf: &mut impl EditBuffer, +) -> D +where + D: FromStr + ToString, +{ let id: &String = &id.into(); let prev_buf = editbuf.get_buf(id, &default.to_string()).clone(); let response = ui.text_edit_singleline(editbuf.get_buf(id, &default.to_string())); @@ -32,4 +39,4 @@ pub fn egui_parsed_textfield(ui: &mut Ui, id: impl Into, default: D, } default -} \ No newline at end of file +} diff --git a/src/user_interface/gui/windows/tool_window.rs b/src/user_interface/gui/windows/tool_window.rs index d982591..f144b0e 100644 --- a/src/user_interface/gui/windows/tool_window.rs +++ b/src/user_interface/gui/windows/tool_window.rs @@ -1,7 +1,9 @@ -use egui::{Context, Align2}; - -use crate::{editor::Editor, user_interface::{Interface, gui::window::GlifWindow}}; +use egui::{Align2, Context}; +use crate::{ + editor::Editor, + user_interface::{gui::window::GlifWindow, Interface}, +}; pub struct ToolWindow { // is this window open? @@ -32,7 +34,9 @@ impl GlifWindow for ToolWindow { populated_ui = v.dispatch_tool_ui(i, ui); }); - if !populated_ui { return }; + if !populated_ui { + return; + }; egui::Window::new("Tool") .resizable(false) @@ -45,4 +49,4 @@ impl GlifWindow for ToolWindow { v.dispatch_tool_ui(i, ui); }); } -} \ No newline at end of file +} diff --git a/src/user_interface/mod.rs b/src/user_interface/mod.rs index 56a4c32..3364fea 100644 --- a/src/user_interface/mod.rs +++ b/src/user_interface/mod.rs @@ -1,9 +1,9 @@ +pub mod egui_manager; pub mod follow; pub mod gui; pub mod icons; pub mod mouse_input; pub mod sdl; -pub mod egui_manager; use std::rc::Rc; @@ -13,14 +13,14 @@ use gl; use glifrenderer::grid::Grid; use glifrenderer::viewport::Viewport; use sdl2::video::GLContext; +use skia_bindings::GrDirectContext; use skia_bindings::GrSurfaceOrigin; +use skia_safe::gpu::gl::FramebufferInfo; +use skia_safe::gpu::BackendRenderTarget; use skia_safe::Color; use skia_safe::ColorType; use skia_safe::RCHandle; -use skia_bindings::GrDirectContext; use skia_safe::Surface; -use skia_safe::gpu::BackendRenderTarget; -use skia_safe::gpu::gl::FramebufferInfo; use crate::editor::Editor; pub use crate::user_interface::mouse_input::MouseInfo; @@ -64,7 +64,7 @@ impl Interface { let fb_info = { let mut fboid = 0; unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) }; - + FramebufferInfo { fboid: fboid.try_into().unwrap(), format: skia_safe::gpu::gl::Format::RGBA8.into(), @@ -88,7 +88,6 @@ impl Interface { fb_info, }; - iself.adjust_viewport_by_os_dpi(); iself @@ -185,7 +184,6 @@ impl Interface { } } - fn create_surface( window: &Window, fb_info: &FramebufferInfo, diff --git a/src/user_interface/sdl.rs b/src/user_interface/sdl.rs index 10a2ce8..f9c61fd 100644 --- a/src/user_interface/sdl.rs +++ b/src/user_interface/sdl.rs @@ -6,15 +6,18 @@ use crate::user_interface::Interface; use glifrenderer::viewport::Viewport; use image; use sdl2::keyboard::Keycode; -use sdl2::EventPump; use sdl2::video::{GLContext, GLProfile}; +use sdl2::EventPump; use sdl2::{pixels::PixelFormatEnum, surface::Surface, video::Window, Sdl}; use skia_bindings::GrDirectContext; use skia_safe::RCHandle; impl Interface { // for macOS, we may mutate viewport.winsize. other OS don't (normally?) mutate viewport - pub fn initialize_sdl(filename: &str, viewport: &mut Viewport) -> (Sdl, Window, RCHandle, GLContext) { + pub fn initialize_sdl( + filename: &str, + viewport: &mut Viewport, + ) -> (Sdl, Window, RCHandle, GLContext) { // SDL initialization let sdl_context = sdl2::init().expect("Failed to initialize sdl2"); let video_subsystem = sdl_context @@ -52,7 +55,7 @@ impl Interface { let gr_context = skia_safe::gpu::DirectContext::new_gl(Some(interface), None).unwrap(); video_subsystem.text_input().start(); - + let logo = include_bytes!("../../resources/icon.png"); let mut im = image::load_from_memory_with_format(logo, image::ImageFormat::Png) @@ -78,9 +81,7 @@ impl Interface { (sdl_context, window, gr_context, gl_ctx) } - fn create_gl_context(&mut self) { - - } + fn create_gl_context(&mut self) {} pub fn set_window_title(&mut self, title: &str) -> Result<(), NulError> { self.sdl_window.set_title(title)