diff --git a/runebender-lib/src/app_delegate.rs b/runebender-lib/src/app_delegate.rs index a029060..c83a2b9 100644 --- a/runebender-lib/src/app_delegate.rs +++ b/runebender-lib/src/app_delegate.rs @@ -62,6 +62,22 @@ impl AppDelegate for Delegate { { data.workspace.rename_glyph(old.clone(), new.clone()); Handled::Yes + } else if let Some(consts::cmd::SetGlyphKernGroupArgs { + glyph_name, + group_key, + }) = cmd.get(consts::cmd::SET_GLYPH_KERN1_GROUP) + { + data.workspace + .set_glyph_group("public.kern1.", glyph_name.clone(), group_key.clone()); + Handled::Yes + } else if let Some(consts::cmd::SetGlyphKernGroupArgs { + glyph_name, + group_key, + }) = cmd.get(consts::cmd::SET_GLYPH_KERN2_GROUP) + { + data.workspace + .set_glyph_group("public.kern2.", glyph_name.clone(), group_key.clone()); + Handled::Yes } else if cmd.is(consts::cmd::NEW_PREVIEW_WINDOW) { let session_id = data.workspace.new_preview_session(); let new_win = WindowDesc::new(make_preview(session_id)) diff --git a/runebender-lib/src/consts.rs b/runebender-lib/src/consts.rs index 2a0fc95..99593bd 100644 --- a/runebender-lib/src/consts.rs +++ b/runebender-lib/src/consts.rs @@ -49,6 +49,20 @@ pub mod cmd { pub new: GlyphName, } + /// Change the kern1 group of a glyph. + pub const SET_GLYPH_KERN1_GROUP: Selector = + Selector::new("runebender.set-glyph-kern1-group"); + + /// Change the kern2 group of a glyph. + pub const SET_GLYPH_KERN2_GROUP: Selector = + Selector::new("runebender.set-glyph-kern2-group"); + + /// Arguments passed with the SET_GLYPH_KERN1_GROUP AND SET_GLYPH_KERN2_GROUP commands. + pub struct SetGlyphKernGroupArgs { + pub glyph_name: GlyphName, + pub group_key: String, + } + /// sent by the 'add component' menu item pub const ADD_COMPONENT: Selector = Selector::new("runebender.add-component"); diff --git a/runebender-lib/src/data.rs b/runebender-lib/src/data.rs index 8121a6b..d7080ad 100644 --- a/runebender-lib/src/data.rs +++ b/runebender-lib/src/data.rs @@ -87,6 +87,8 @@ pub struct GlyphDetail { // the full outline, including things like components pub outline: Arc, metrics: FontMetrics, + pub kern1_group: String, + pub kern2_group: String, is_placeholder: bool, } @@ -356,6 +358,33 @@ impl Workspace { } } + pub fn set_glyph_group(&mut self, prefix: &str, glyph_name: GlyphName, group_name: String) { + let font = self.font_mut(); + if let Some(mut groups) = font.ufo.groups.clone() { + let mut do_remove = Vec::::new(); // BTreeMap retain is nightly for now + for (gk, gv) in groups.iter_mut() { + if let Some(gn) = gk.strip_prefix(prefix) { + if gn != group_name { + (*gv).retain(|n| *n != glyph_name); + if (*gv).len() == 0 { + do_remove.push(gk.clone()); + } + } + } + } + for gk in do_remove { + groups.remove(&gk); + } + if group_name != "" { + let k = [prefix, &group_name].join(""); + let entry = groups.entry(k).or_insert_with(Vec::::new); + entry.push(glyph_name); + entry.sort(); + } + font.ufo.groups = Some(groups); + } + } + pub fn update_glyph_metadata(&mut self, changed: &Arc) { // update the active session, if one exists if let Some(session_id) = self.session_map.get(&changed.name) { @@ -599,6 +628,7 @@ mod lenses { use std::sync::Arc; use druid::{Data, Lens}; + use norad::glyph::Glyph; use norad::GlyphName as GlyphName_; use super::{ @@ -722,10 +752,13 @@ mod lenses { let outline = state.font.get_bezier(&glyph.name); let is_placeholder = outline.is_none(); let metrics = state.font.info.metrics.clone(); + let (kern1_group, kern2_group) = find_kern_groups(&state.font, &glyph); GlyphDetail { glyph, outline: outline.unwrap_or_else(|| state.font.font.placeholder.clone()), is_placeholder, + kern1_group, + kern2_group, metrics, } } @@ -762,10 +795,13 @@ mod lenses { let outline = data.get_bezier(&glyph.name); let is_placeholder = outline.is_none(); let metrics = data.info.metrics.clone(); + let (kern1_group, kern2_group) = find_kern_groups(data, glyph); GlyphDetail { glyph: Arc::clone(glyph), outline: outline.unwrap_or_else(|| data.font.placeholder.clone()), metrics, + kern1_group, + kern2_group, is_placeholder, } }); @@ -786,10 +822,13 @@ mod lenses { let outline = data.get_bezier(&glyph.name); let is_placeholder = outline.is_none(); let metrics = data.info.metrics.clone(); + let (kern1_group, kern2_group) = find_kern_groups(data, glyph); GlyphDetail { glyph: Arc::clone(glyph), outline: outline.unwrap_or_else(|| data.font.placeholder.clone()), metrics, + kern1_group, + kern2_group, is_placeholder, } }); @@ -911,6 +950,25 @@ mod lenses { f(&mut s) } } + + fn find_kern_groups(data: &Workspace, glyph: &Glyph) -> (String, String) { + let mut kern1_group = "".to_string(); + let mut kern2_group = "".to_string(); + if let Some(gs) = &data.font.ufo.groups { + for (gk, gv) in gs.iter() { + if let Some(gn) = gk.strip_prefix("public.kern1.") { + if (*gv).iter().any(|n| *n == glyph.name) { + kern1_group = gn.to_string(); + } + } else if let Some(gn) = gk.strip_prefix("public.kern2.") { + if (*gv).iter().any(|n| *n == glyph.name) { + kern2_group = gn.to_string(); + } + } + } + } + (kern1_group, kern2_group) + } } //FIXME: put this in some `GlyphExt` trait or something diff --git a/runebender-lib/src/widgets/sidebar.rs b/runebender-lib/src/widgets/sidebar.rs index 9c1947a..04e3a35 100644 --- a/runebender-lib/src/widgets/sidebar.rs +++ b/runebender-lib/src/widgets/sidebar.rs @@ -63,13 +63,13 @@ fn selected_glyph_widget() -> impl Widget { .with_child( Flex::row() .with_child( - Label::new("kern group") + Label::dynamic(|d: &GlyphDetail, _| (*d.kern2_group).to_string()) .with_text_color(theme::SECONDARY_TEXT_COLOR) .with_font(theme::UI_DETAIL_FONT), ) .with_flex_spacer(1.0) .with_child( - Label::new("kern group") + Label::dynamic(|d: &GlyphDetail, _| (*d.kern1_group).to_string()) .with_text_color(theme::SECONDARY_TEXT_COLOR) .with_font(theme::UI_DETAIL_FONT), )