mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 05:18:11 +08:00
Make font selection show a live preview on hover; move its code to the backend (#3487)
* Remove FontInput.svelte * Move font picking to the backend * Fix Text tool font choice style turning to "-" on font that doesn't support previous style
This commit is contained in:
@@ -2,7 +2,6 @@ use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
use graphene_std::raster::color::Color;
|
||||
use graphene_std::text::Font;
|
||||
use graphene_std::vector::style::{FillChoice, GradientStops};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
@@ -48,7 +47,6 @@ impl MessageHandler<LayoutMessage, LayoutMessageContext<'_>> for LayoutMessageHa
|
||||
}
|
||||
LayoutMessage::WidgetValueUpdate { layout_target, widget_id, value } => {
|
||||
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Update, responses);
|
||||
responses.add(LayoutMessage::ResendActiveWidget { layout_target, widget_id });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,44 +262,6 @@ impl LayoutMessageHandler {
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::FontInput(font_input) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (font_input.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
let Some(update_value) = value.as_object() else {
|
||||
error!("FontInput update was not of type: object");
|
||||
return;
|
||||
};
|
||||
let Some(font_family_value) = update_value.get("fontFamily") else {
|
||||
error!("FontInput update does not have a fontFamily");
|
||||
return;
|
||||
};
|
||||
let Some(font_style_value) = update_value.get("fontStyle") else {
|
||||
error!("FontInput update does not have a fontStyle");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(font_family) = font_family_value.as_str() else {
|
||||
error!("FontInput update fontFamily was not of type: string");
|
||||
return;
|
||||
};
|
||||
let Some(font_style) = font_style_value.as_str() else {
|
||||
error!("FontInput update fontStyle was not of type: string");
|
||||
return;
|
||||
};
|
||||
|
||||
font_input.font_family = font_family.into();
|
||||
font_input.font_style = font_style.into();
|
||||
|
||||
responses.add(PortfolioMessage::LoadFont {
|
||||
font: Font::new(font_family.into(), font_style.into()),
|
||||
});
|
||||
(font_input.on_update.callback)(font_input)
|
||||
}
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::IconButton(icon_button) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (icon_button.on_commit.callback)(&()),
|
||||
|
||||
@@ -353,43 +353,7 @@ impl From<Vec<WidgetInstance>> for LayoutGroup {
|
||||
}
|
||||
|
||||
impl LayoutGroup {
|
||||
/// Applies a tooltip label to all widgets in this row or column without a tooltip.
|
||||
pub fn with_tooltip_label(self, label: impl Into<String>) -> Self {
|
||||
let (is_col, mut widgets) = match self {
|
||||
LayoutGroup::Column { widgets } => (true, widgets),
|
||||
LayoutGroup::Row { widgets } => (false, widgets),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let label = label.into();
|
||||
for widget in &mut widgets {
|
||||
let val = match &mut widget.widget {
|
||||
Widget::CheckboxInput(x) => &mut x.tooltip_label,
|
||||
Widget::ColorInput(x) => &mut x.tooltip_label,
|
||||
Widget::CurveInput(x) => &mut x.tooltip_label,
|
||||
Widget::DropdownInput(x) => &mut x.tooltip_label,
|
||||
Widget::FontInput(x) => &mut x.tooltip_label,
|
||||
Widget::IconButton(x) => &mut x.tooltip_label,
|
||||
Widget::IconLabel(x) => &mut x.tooltip_label,
|
||||
Widget::ImageButton(x) => &mut x.tooltip_label,
|
||||
Widget::ImageLabel(x) => &mut x.tooltip_label,
|
||||
Widget::NumberInput(x) => &mut x.tooltip_label,
|
||||
Widget::ParameterExposeButton(x) => &mut x.tooltip_label,
|
||||
Widget::PopoverButton(x) => &mut x.tooltip_label,
|
||||
Widget::TextAreaInput(x) => &mut x.tooltip_label,
|
||||
Widget::TextButton(x) => &mut x.tooltip_label,
|
||||
Widget::TextInput(x) => &mut x.tooltip_label,
|
||||
Widget::TextLabel(x) => &mut x.tooltip_label,
|
||||
Widget::BreadcrumbTrailButtons(x) => &mut x.tooltip_label,
|
||||
Widget::ReferencePointInput(_) | Widget::RadioInput(_) | Widget::Separator(_) | Widget::ShortcutLabel(_) | Widget::WorkingColorsInput(_) | Widget::NodeCatalog(_) => continue,
|
||||
};
|
||||
if val.is_empty() {
|
||||
val.clone_from(&label);
|
||||
}
|
||||
}
|
||||
if is_col { Self::Column { widgets } } else { Self::Row { widgets } }
|
||||
}
|
||||
|
||||
/// Applies a tooltip description to all widgets in this row or column without a tooltip.
|
||||
/// Applies a tooltip description to all widgets without a tooltip in this row or column.
|
||||
pub fn with_tooltip_description(self, description: impl Into<String>) -> Self {
|
||||
let (is_col, mut widgets) = match self {
|
||||
LayoutGroup::Column { widgets } => (true, widgets),
|
||||
@@ -403,20 +367,24 @@ impl LayoutGroup {
|
||||
Widget::ColorInput(x) => &mut x.tooltip_description,
|
||||
Widget::CurveInput(x) => &mut x.tooltip_description,
|
||||
Widget::DropdownInput(x) => &mut x.tooltip_description,
|
||||
Widget::FontInput(x) => &mut x.tooltip_description,
|
||||
Widget::IconButton(x) => &mut x.tooltip_description,
|
||||
Widget::IconLabel(x) => &mut x.tooltip_description,
|
||||
Widget::ImageButton(x) => &mut x.tooltip_description,
|
||||
Widget::ImageLabel(x) => &mut x.tooltip_description,
|
||||
Widget::NumberInput(x) => &mut x.tooltip_description,
|
||||
Widget::ParameterExposeButton(x) => &mut x.tooltip_description,
|
||||
Widget::PopoverButton(x) => &mut x.tooltip_description,
|
||||
Widget::TextAreaInput(x) => &mut x.tooltip_description,
|
||||
Widget::TextButton(x) => &mut x.tooltip_description,
|
||||
Widget::TextInput(x) => &mut x.tooltip_description,
|
||||
Widget::TextLabel(x) => &mut x.tooltip_description,
|
||||
Widget::BreadcrumbTrailButtons(x) => &mut x.tooltip_description,
|
||||
Widget::ReferencePointInput(_) | Widget::RadioInput(_) | Widget::Separator(_) | Widget::ShortcutLabel(_) | Widget::WorkingColorsInput(_) | Widget::NodeCatalog(_) => continue,
|
||||
Widget::ReferencePointInput(_)
|
||||
| Widget::RadioInput(_)
|
||||
| Widget::Separator(_)
|
||||
| Widget::ShortcutLabel(_)
|
||||
| Widget::WorkingColorsInput(_)
|
||||
| Widget::NodeCatalog(_)
|
||||
| Widget::ParameterExposeButton(_) => continue,
|
||||
};
|
||||
if val.is_empty() {
|
||||
val.clone_from(&description);
|
||||
@@ -727,7 +695,6 @@ pub enum Widget {
|
||||
ColorInput(ColorInput),
|
||||
CurveInput(CurveInput),
|
||||
DropdownInput(DropdownInput),
|
||||
FontInput(FontInput),
|
||||
IconButton(IconButton),
|
||||
IconLabel(IconLabel),
|
||||
ImageButton(ImageButton),
|
||||
@@ -782,7 +749,6 @@ impl DiffUpdate {
|
||||
Widget::CheckboxInput(widget) => widget.tooltip_shortcut.as_mut(),
|
||||
Widget::ColorInput(widget) => widget.tooltip_shortcut.as_mut(),
|
||||
Widget::DropdownInput(widget) => widget.tooltip_shortcut.as_mut(),
|
||||
Widget::FontInput(widget) => widget.tooltip_shortcut.as_mut(),
|
||||
Widget::IconButton(widget) => widget.tooltip_shortcut.as_mut(),
|
||||
Widget::NumberInput(widget) => widget.tooltip_shortcut.as_mut(),
|
||||
Widget::ParameterExposeButton(widget) => widget.tooltip_shortcut.as_mut(),
|
||||
@@ -838,10 +804,38 @@ impl DiffUpdate {
|
||||
(recursive_wrapper.0)(entry_sections, &recursive_wrapper)
|
||||
};
|
||||
|
||||
// Hash the menu list entry sections for caching purposes
|
||||
let hash_menu_list_entry_sections = |entry_sections: &MenuListEntrySections| {
|
||||
struct RecursiveHasher<'a> {
|
||||
hasher: DefaultHasher,
|
||||
hash_fn: &'a dyn Fn(&mut RecursiveHasher, &MenuListEntrySections),
|
||||
}
|
||||
let mut recursive_hasher = RecursiveHasher {
|
||||
hasher: DefaultHasher::new(),
|
||||
hash_fn: &|recursive_hasher, entry_sections| {
|
||||
for (index, entries) in entry_sections.iter().enumerate() {
|
||||
index.hash(&mut recursive_hasher.hasher);
|
||||
for entry in entries {
|
||||
entry.hash(&mut recursive_hasher.hasher);
|
||||
(recursive_hasher.hash_fn)(recursive_hasher, &entry.children);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
(recursive_hasher.hash_fn)(&mut recursive_hasher, entry_sections);
|
||||
recursive_hasher.hasher.finish()
|
||||
};
|
||||
|
||||
// Apply shortcut conversions to all widgets that have menu lists
|
||||
let convert_menu_lists = |widget_instance: &mut WidgetInstance| match &mut widget_instance.widget {
|
||||
Widget::DropdownInput(dropdown_input) => apply_action_shortcut_to_menu_lists(&mut dropdown_input.entries),
|
||||
Widget::TextButton(text_button) => apply_action_shortcut_to_menu_lists(&mut text_button.menu_list_children),
|
||||
Widget::DropdownInput(dropdown_input) => {
|
||||
apply_action_shortcut_to_menu_lists(&mut dropdown_input.entries);
|
||||
dropdown_input.entries_hash = hash_menu_list_entry_sections(&dropdown_input.entries);
|
||||
}
|
||||
Widget::TextButton(text_button) => {
|
||||
apply_action_shortcut_to_menu_lists(&mut text_button.menu_list_children);
|
||||
text_button.menu_list_children_hash = hash_menu_list_entry_sections(&text_button.menu_list_children);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
|
||||
@@ -144,6 +144,10 @@ pub struct TextButton {
|
||||
#[serde(rename = "menuListChildren")]
|
||||
pub menu_list_children: MenuListEntrySections,
|
||||
|
||||
#[serde(rename = "menuListChildrenHash")]
|
||||
#[widget_builder(skip)]
|
||||
pub menu_list_children_hash: u64,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
|
||||
@@ -80,6 +80,10 @@ pub struct DropdownInput {
|
||||
#[widget_builder(constructor)]
|
||||
pub entries: MenuListEntrySections,
|
||||
|
||||
#[serde(rename = "entriesHash")]
|
||||
#[widget_builder(skip)]
|
||||
pub entries_hash: u64,
|
||||
|
||||
// This uses `u32` instead of `usize` since it will be serialized as a normal JS number (replace this with `usize` after switching to a Rust-based GUI)
|
||||
#[serde(rename = "selectedIndex")]
|
||||
pub selected_index: Option<u32>,
|
||||
@@ -94,6 +98,9 @@ pub struct DropdownInput {
|
||||
|
||||
pub narrow: bool,
|
||||
|
||||
#[serde(rename = "virtualScrolling")]
|
||||
pub virtual_scrolling: bool,
|
||||
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
@@ -142,6 +149,10 @@ pub struct MenuListEntry {
|
||||
|
||||
pub children: MenuListEntrySections,
|
||||
|
||||
#[serde(rename = "childrenHash")]
|
||||
#[widget_builder(skip)]
|
||||
pub children_hash: u64,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
@@ -152,39 +163,13 @@ pub struct MenuListEntry {
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
|
||||
#[derivative(Debug, PartialEq, Default)]
|
||||
pub struct FontInput {
|
||||
#[serde(rename = "fontFamily")]
|
||||
#[widget_builder(constructor)]
|
||||
pub font_family: String,
|
||||
|
||||
#[serde(rename = "fontStyle")]
|
||||
#[widget_builder(constructor)]
|
||||
pub font_style: String,
|
||||
|
||||
#[serde(rename = "isStyle")]
|
||||
pub is_style_picker: bool,
|
||||
|
||||
pub disabled: bool,
|
||||
|
||||
#[serde(rename = "tooltipLabel")]
|
||||
pub tooltip_label: String,
|
||||
|
||||
#[serde(rename = "tooltipDescription")]
|
||||
pub tooltip_description: String,
|
||||
|
||||
#[serde(rename = "tooltipShortcut")]
|
||||
pub tooltip_shortcut: Option<ActionShortcut>,
|
||||
|
||||
// Callbacks
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<FontInput>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
impl std::hash::Hash for MenuListEntry {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.value.hash(state);
|
||||
self.label.hash(state);
|
||||
self.icon.hash(state);
|
||||
self.disabled.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)]
|
||||
|
||||
Reference in New Issue
Block a user