diff --git a/crates/app/src/message.rs b/crates/app/src/message.rs index 3548f9b..8ed6fa8 100644 --- a/crates/app/src/message.rs +++ b/crates/app/src/message.rs @@ -78,6 +78,22 @@ pub enum StyleSetting { DefaultPressedKeyOutlineWidth(u32), DefaultPressedKeyBackgroundImage, KeyboardBackgroundImage, + LooseKeyFontFamily(u32), + LooseKeyShowOutline(u32), + LooseKeyOutlineWidth { id: u32, width: u32 }, + LooseKeyBackgroundImage(u32), + LooseKeyBold(u32), + LooseKeyItalic(u32), + LooseKeyUnderline(u32), + LooseKeyStrikethrough(u32), + PressedKeyFontFamily(u32), + PressedKeyShowOutline(u32), + PressedKeyOutlineWidth { id: u32, width: u32 }, + PressedKeyBackgroundImage(u32), + PressedKeyBold(u32), + PressedKeyItalic(u32), + PressedKeyUnderline(u32), + PressedKeyStrikethrough(u32), } // TODO: Are window resized undoable in NohBoard? diff --git a/crates/app/src/nuhxboard.rs b/crates/app/src/nuhxboard.rs index 4515564..8db0929 100644 --- a/crates/app/src/nuhxboard.rs +++ b/crates/app/src/nuhxboard.rs @@ -1,10 +1,7 @@ use crate::{ message::*, nuhxboard_types::*, - ui::{ - app::*, - popups::{self, *}, - }, + ui::{app::*, popups::*}, }; use async_std::task::sleep; use display_info::DisplayInfo; @@ -13,7 +10,7 @@ use iced::{ advanced::graphics::core::SmolStr, widget::canvas::Cache, window, Renderer, Subscription, Task, Theme, }; -use iced_multi_window::{Window, WindowManager}; +use iced_multi_window::WindowManager; use image::ImageReader; use logic::{code_convert::*, listener}; use std::{ @@ -361,80 +358,290 @@ impl NuhxBoard { match input { TextInputType::SaveStyleAsName => self.text_input.save_style_as_name = value, TextInputType::SaveKeyboardAsName => { - self.text_input.save_keyboard_as_name = value + self.text_input.save_keyboard_as_name = value; } TextInputType::SaveKeyboardAsCategory => { - self.text_input.save_keyboard_as_category = value + self.text_input.save_keyboard_as_category = value; } TextInputType::KeyboardBackgroundImage => { - self.text_input.keyboard_background_image = value + self.text_input.keyboard_background_image = value; } TextInputType::DefaultLooseKeyBackgroundImage => { - self.text_input.default_loose_key_background_image = value + self.text_input.default_loose_key_background_image = value; } TextInputType::DefaultLooseKeyFontFamily => { - self.text_input.default_loose_key_font_family = value + self.text_input.default_loose_key_font_family = value; } TextInputType::DefaultPressedKeyBackgroundImage => { - self.text_input.default_pressed_key_background_image = value + self.text_input.default_pressed_key_background_image = value; } TextInputType::DefaultPressedKeyFontFamily => { - self.text_input.default_pressed_key_font_family = value + self.text_input.default_pressed_key_font_family = value; + } + TextInputType::LooseBackgroundImage(id) => { + self.text_input.loose_background_image.insert(id, value); + } + TextInputType::LooseFontFamily(id) => { + self.text_input.loose_font_family.insert(id, value); + } + TextInputType::PressedBackgroundImage(id) => { + self.text_input.pressed_background_image.insert(id, value); + } + TextInputType::PressedFontFamily(id) => { + self.text_input.pressed_font_family.insert(id, value); } } clear_canvas = false; } - Message::ChangeStyle(style) => match style { - StyleSetting::DefaultMouseSpeedIndicatorOutlineWidth(width) => { - self.style.default_mouse_speed_indicator_style.outline_width = width; - } - StyleSetting::DefaultLooseKeyFontFamily => { - self.style.default_key_style.loose.font.font_family = - self.text_input.default_loose_key_font_family.clone(); - } - StyleSetting::DefaultLooseKeyShowOutline => { - self.style.default_key_style.loose.show_outline = - !self.style.default_key_style.loose.show_outline; - } - StyleSetting::DefaultLooseKeyOutlineWidth(width) => { - self.style.default_key_style.loose.outline_width = width; - } - StyleSetting::DefaultLooseKeyBackgroundImage => { - let image = self.text_input.default_loose_key_background_image.clone(); - self.style - .default_key_style - .loose - .background_image_file_name = - if image.is_empty() { None } else { Some(image) }; - } - StyleSetting::DefaultPressedKeyFontFamily => { - self.style.default_key_style.pressed.font.font_family = - self.text_input.default_pressed_key_font_family.clone(); - } - StyleSetting::DefaultPressedKeyShowOutline => { - self.style.default_key_style.pressed.show_outline = - !self.style.default_key_style.pressed.show_outline; - } - StyleSetting::DefaultPressedKeyOutlineWidth(width) => { - self.style.default_key_style.pressed.outline_width = width; - } - StyleSetting::DefaultPressedKeyBackgroundImage => { - let image = self.text_input.default_pressed_key_background_image.clone(); - self.style - .default_key_style - .pressed - .background_image_file_name = - if image.is_empty() { None } else { Some(image) }; + Message::ChangeStyle(style) => { + macro_rules! key_style_change { + ($name:ident, $block:block, $id:ident) => { + let mut $name = self.style.default_key_style.clone(); + $block + self.style + .element_styles + .entry($id) + .and_modify(|$name| { + let style::ElementStyle::KeyStyle(ref mut $name) = $name else { + panic!() + }; + $block + }) + .or_insert(style::ElementStyle::KeyStyle($name)); + } } - StyleSetting::KeyboardBackgroundImage => { - let image = self.text_input.keyboard_background_image.clone(); - self.change_background_image(Some(if image.is_empty() { - None - } else { - Some(image) - })); + match style { + StyleSetting::DefaultMouseSpeedIndicatorOutlineWidth(width) => { + self.style.default_mouse_speed_indicator_style.outline_width = width; + } + StyleSetting::DefaultLooseKeyFontFamily => { + let new_font = self.text_input.default_loose_key_font_family.clone(); + FONTS.write().unwrap().insert(new_font.clone().leak()); + self.style.default_key_style.loose.font.font_family = new_font; + } + StyleSetting::DefaultLooseKeyShowOutline => { + self.style.default_key_style.loose.show_outline = + !self.style.default_key_style.loose.show_outline; + } + StyleSetting::DefaultLooseKeyOutlineWidth(width) => { + self.style.default_key_style.loose.outline_width = width; + } + StyleSetting::DefaultLooseKeyBackgroundImage => { + let image = self.text_input.default_loose_key_background_image.clone(); + self.style + .default_key_style + .loose + .background_image_file_name = + if image.is_empty() { None } else { Some(image) }; + } + StyleSetting::DefaultPressedKeyFontFamily => { + let new_font = self.text_input.default_pressed_key_font_family.clone(); + FONTS.write().unwrap().insert(new_font.clone().leak()); + self.style.default_key_style.pressed.font.font_family = new_font; + } + StyleSetting::DefaultPressedKeyShowOutline => { + self.style.default_key_style.pressed.show_outline = + !self.style.default_key_style.pressed.show_outline; + } + StyleSetting::DefaultPressedKeyOutlineWidth(width) => { + self.style.default_key_style.pressed.outline_width = width; + } + StyleSetting::DefaultPressedKeyBackgroundImage => { + let image = self.text_input.default_pressed_key_background_image.clone(); + self.style + .default_key_style + .pressed + .background_image_file_name = + if image.is_empty() { None } else { Some(image) }; + } + StyleSetting::KeyboardBackgroundImage => { + let image = self.text_input.keyboard_background_image.clone(); + self.change_background_image(Some(if image.is_empty() { + None + } else { + Some(image) + })); + } + StyleSetting::LooseKeyFontFamily(id) => { + let new_font = self + .text_input + .loose_font_family + .get(&id) + .cloned() + .unwrap_or_default(); + FONTS.write().unwrap().insert(new_font.clone().leak()); + key_style_change!( + style, + { + style.loose.font.font_family = new_font.clone(); + }, + id + ); + } + StyleSetting::LooseKeyShowOutline(id) => { + key_style_change!( + style, + { + style.loose.show_outline = !style.loose.show_outline; + }, + id + ); + } + StyleSetting::LooseKeyOutlineWidth { id, width } => { + key_style_change!( + style, + { + style.loose.outline_width = width; + }, + id + ); + } + StyleSetting::LooseKeyBackgroundImage(id) => { + let image = self + .text_input + .loose_background_image + .get(&id) + .cloned() + .unwrap_or_default(); + key_style_change!( + style, + { + style.loose.background_image_file_name = if image.is_empty() { + None + } else { + Some(image.clone()) + }; + }, + id + ); + } + StyleSetting::PressedKeyFontFamily(id) => { + let new_font = self + .text_input + .pressed_font_family + .get(&id) + .cloned() + .unwrap_or_default(); + FONTS.write().unwrap().insert(new_font.clone().leak()); + key_style_change!( + style, + { + style.pressed.font.font_family = new_font.clone(); + }, + id + ); + } + StyleSetting::PressedKeyShowOutline(id) => { + key_style_change!( + style, + { + style.pressed.show_outline = !style.pressed.show_outline; + }, + id + ); + } + StyleSetting::PressedKeyOutlineWidth { id, width } => { + key_style_change!( + style, + { + style.pressed.outline_width = width; + }, + id + ); + } + StyleSetting::PressedKeyBackgroundImage(id) => { + let image = self + .text_input + .pressed_background_image + .get(&id) + .cloned() + .unwrap_or_default(); + key_style_change!( + style, + { + style.pressed.background_image_file_name = if image.is_empty() { + None + } else { + Some(image.clone()) + }; + }, + id + ); + } + StyleSetting::LooseKeyBold(id) => { + key_style_change!( + style, + { + style.loose.font.style ^= 1 << 0; + }, + id + ); + } + StyleSetting::LooseKeyItalic(id) => { + key_style_change!( + style, + { + style.loose.font.style ^= 1 << 1; + }, + id + ); + } + StyleSetting::LooseKeyUnderline(id) => { + key_style_change!( + style, + { + style.loose.font.style ^= 1 << 2; + }, + id + ); + } + StyleSetting::LooseKeyStrikethrough(id) => { + key_style_change!( + style, + { + style.loose.font.style ^= 1 << 3; + }, + id + ); + } + StyleSetting::PressedKeyBold(id) => { + key_style_change!( + style, + { + style.pressed.font.style ^= 1 << 0; + }, + id + ); + } + StyleSetting::PressedKeyItalic(id) => { + key_style_change!( + style, + { + style.pressed.font.style ^= 1 << 1; + }, + id + ); + } + StyleSetting::PressedKeyUnderline(id) => { + key_style_change!( + style, + { + style.pressed.font.style ^= 1 << 2; + }, + id + ); + } + StyleSetting::PressedKeyStrikethrough(id) => { + key_style_change!( + style, + { + style.pressed.font.style ^= 1 << 3; + }, + id + ); + } } - }, + } Message::ToggleSaveStyleAsGlobal => { self.save_style_as_global = !self.save_style_as_global; clear_canvas = false; diff --git a/crates/app/src/nuhxboard_types.rs b/crates/app/src/nuhxboard_types.rs index cb9207f..acf84ad 100644 --- a/crates/app/src/nuhxboard_types.rs +++ b/crates/app/src/nuhxboard_types.rs @@ -38,6 +38,10 @@ pub struct TextInput { pub default_loose_key_font_family: String, pub default_pressed_key_background_image: String, pub default_pressed_key_font_family: String, + pub loose_background_image: HashMap, + pub loose_font_family: HashMap, + pub pressed_background_image: HashMap, + pub pressed_font_family: HashMap, } #[derive(Clone, Debug)] @@ -50,6 +54,10 @@ pub enum TextInputType { DefaultLooseKeyFontFamily, DefaultPressedKeyBackgroundImage, DefaultPressedKeyFontFamily, + LooseBackgroundImage(u32), + LooseFontFamily(u32), + PressedBackgroundImage(u32), + PressedFontFamily(u32), } #[derive(Default)] diff --git a/crates/app/src/ui/app.rs b/crates/app/src/ui/app.rs index 8e1d892..154e219 100644 --- a/crates/app/src/ui/app.rs +++ b/crates/app/src/ui/app.rs @@ -10,7 +10,7 @@ use iced::{ use iced_aw::{number_input, ContextMenu, SelectionList}; use iced_multi_window::Window; use std::sync::Arc; -use types::{config::get_id, settings::*}; +use types::settings::*; static IMAGE: &[u8] = include_bytes!("../../../../NuhxBoard.png"); @@ -190,7 +190,7 @@ impl Window for Main { .into(), context_menu_button("Element Style") .on_press_maybe(if let Some(index) = app.hovered_element { - let id = get_id(&app.layout.elements[index]); + let id = app.layout.elements[index].id(); let window = ElementStyle { id }; (!app.windows.any_of(&window)) .then_some(Message::Open(Box::new(window))) diff --git a/crates/app/src/ui/popups/edit_mode.rs b/crates/app/src/ui/popups/edit_mode.rs index 279c3f3..813e159 100644 --- a/crates/app/src/ui/popups/edit_mode.rs +++ b/crates/app/src/ui/popups/edit_mode.rs @@ -7,7 +7,7 @@ use iced::{ use iced_aw::{helpers::selection_list_with, number_input, selection_list}; use iced_multi_window::Window; use types::{ - config::{get_id, BoardElement, CommonDefinitionRef, SerializablePoint}, + config::{BoardElement, CommonDefinitionRef, SerializablePoint}, style, }; @@ -962,7 +962,29 @@ impl Window for ElementStyle { } fn title(&self, app: &NuhxBoard) -> String { - "Style".into() + let default = match app + .layout + .elements + .iter() + .find(move |v| v.id() == self.id) + .unwrap() + { + BoardElement::KeyboardKey(_) + | BoardElement::MouseKey(_) + | BoardElement::MouseScroll(_) => { + style::ElementStyle::KeyStyle(app.style.default_key_style.clone()) + } + BoardElement::MouseSpeedIndicator(_) => style::ElementStyle::MouseSpeedIndicatorStyle( + app.style.default_mouse_speed_indicator_style.clone(), + ), + }; + let style = app.style.element_styles.get(&self.id).unwrap_or(&default); + match style { + style::ElementStyle::KeyStyle(_) => "Key Style".into(), + style::ElementStyle::MouseSpeedIndicatorStyle(_) => { + "Mouse Speed Indicator Style".into() + } + } } fn theme(&self, _app: &NuhxBoard) -> Theme { @@ -970,25 +992,27 @@ impl Window for ElementStyle { } fn view<'a>(&'a self, app: &'a NuhxBoard) -> iced::Element<'a, Message, Theme> { - match app + let id = self.id; + let default = match app .layout .elements .iter() - .find(|e| get_id(e) == self.id) + .find(move |v| v.id() == id) .unwrap() { - BoardElement::KeyboardKey(_) => { - let style = app - .style - .element_styles - .get(&self.id) - .map(|v| { - let style::ElementStyle::KeyStyle(ref key) = v else { - panic!() - }; - key - }) - .unwrap_or(&app.style.default_key_style); + BoardElement::KeyboardKey(_) + | BoardElement::MouseKey(_) + | BoardElement::MouseScroll(_) => { + style::ElementStyle::KeyStyle(app.style.default_key_style.clone()) + } + BoardElement::MouseSpeedIndicator(_) => style::ElementStyle::MouseSpeedIndicatorStyle( + app.style.default_mouse_speed_indicator_style.clone(), + ), + }; + let style = app.style.element_styles.get(&self.id).unwrap_or(&default); + + match style { + style::ElementStyle::KeyStyle(style) => { let column_1 = column![ category_label("Loose"), text("Background"), @@ -1004,7 +1028,23 @@ impl Window for ElementStyle { ColorPicker::LooseBackground, self.id ), - labeled_text_input("Image", text_input("", "")), + labeled_text_input( + "Image", + text_input( + "", + app.text_input + .loose_background_image + .get(&self.id) + .map(|v| v.as_str()) + .unwrap_or_default() + ) + .on_input(move |v| { + Message::ChangeTextInput(TextInputType::LooseBackgroundImage(id), v) + }) + .on_submit(Message::ChangeStyle( + StyleSetting::LooseKeyBackgroundImage(self.id) + )) + ), ]), text("Text"), gray_box(column![ @@ -1045,13 +1085,40 @@ impl Window for ElementStyle { .underline(font.style & 0b0100 != 0) .strikethrough(font.style & 0b1000 != 0)] }, + labeled_text_input( + "Font Family: ", + text_input( + "", + app.text_input + .loose_font_family + .get(&self.id) + .map(|v| v.as_str()) + .unwrap_or_default() + ) + .on_input(move |v| Message::ChangeTextInput( + TextInputType::LooseFontFamily(id), + v + )) + .on_submit(Message::ChangeStyle(StyleSetting::LooseKeyFontFamily( + self.id + ))) + ), row![ - checkbox("Bold", style.loose.font.style & 1 != 0), - checkbox("Italic", style.loose.font.style & 0b10 != 0), + checkbox("Bold", style.loose.font.style & 1 != 0).on_toggle( + move |_| Message::ChangeStyle(StyleSetting::LooseKeyBold(id)) + ), + checkbox("Italic", style.loose.font.style & 0b10 != 0).on_toggle( + move |_| Message::ChangeStyle(StyleSetting::LooseKeyItalic(id)) + ), ], row![ - checkbox("Underline", style.loose.font.style & 0b100 != 0), - checkbox("Strikethrough", style.loose.font.style & 0b1000 != 0), + checkbox("Underline", style.loose.font.style & 0b100 != 0).on_toggle( + move |_| Message::ChangeStyle(StyleSetting::LooseKeyUnderline(id)) + ), + checkbox("Strikethrough", style.loose.font.style & 0b1000 != 0) + .on_toggle(move |_| Message::ChangeStyle( + StyleSetting::LooseKeyStrikethrough(id) + )), ] ]), text("Outline"), @@ -1067,13 +1134,19 @@ impl Window for ElementStyle { ColorPicker::LooseOutline, self.id ), - checkbox("Show Outline", style.loose.show_outline), + checkbox("Show Outline", style.loose.show_outline).on_toggle(move |_| { + Message::ChangeStyle(StyleSetting::LooseKeyShowOutline(id)) + }), row![ - number_input(style.loose.outline_width, 1.., |_| Message::none()), + number_input(style.loose.outline_width, 1.., move |v| { + Message::ChangeStyle(StyleSetting::LooseKeyOutlineWidth { + id, + width: v, + }) + }), text(" Outline Width"), ] ]), - checkbox("Overwrite default style", true) ]; let column_2 = column![ @@ -1091,7 +1164,26 @@ impl Window for ElementStyle { ColorPicker::PressedBackground, self.id ), - labeled_text_input("Image", text_input("", "")), + labeled_text_input( + "Image", + text_input( + "", + app.text_input + .pressed_background_image + .get(&self.id) + .map(|v| v.as_str()) + .unwrap_or_default() + ) + .on_input(move |v| { + Message::ChangeTextInput( + TextInputType::PressedBackgroundImage(id), + v, + ) + }) + .on_submit(Message::ChangeStyle( + StyleSetting::PressedKeyBackgroundImage(self.id) + )) + ), ]), text("Text"), gray_box(column![ @@ -1132,13 +1224,39 @@ impl Window for ElementStyle { .underline(font.style & 0b0100 != 0) .strikethrough(font.style & 0b1000 != 0)] }, + text_input( + "", + app.text_input + .pressed_font_family + .get(&self.id) + .map(|v| v.as_str()) + .unwrap_or_default() + ) + .on_input(move |v| Message::ChangeTextInput( + TextInputType::PressedFontFamily(id), + v + )) + .on_submit(Message::ChangeStyle( + StyleSetting::PressedKeyFontFamily(self.id) + )), row![ - checkbox("Bold", style.pressed.font.style & 1 != 0), - checkbox("Italic", style.pressed.font.style & 0b10 != 0), + checkbox("Bold", style.pressed.font.style & 1 != 0).on_toggle( + move |_| Message::ChangeStyle(StyleSetting::PressedKeyBold(id)) + ), + checkbox("Italic", style.pressed.font.style & 0b10 != 0).on_toggle( + move |_| Message::ChangeStyle(StyleSetting::PressedKeyItalic(id)) + ), ], row![ - checkbox("Underline", style.pressed.font.style & 0b100 != 0), - checkbox("Strikethrough", style.pressed.font.style & 0b1000 != 0), + checkbox("Underline", style.pressed.font.style & 0b100 != 0).on_toggle( + move |_| Message::ChangeStyle(StyleSetting::PressedKeyUnderline( + id + )) + ), + checkbox("Strikethrough", style.pressed.font.style & 0b1000 != 0) + .on_toggle(move |_| Message::ChangeStyle( + StyleSetting::PressedKeyStrikethrough(id) + )), ] ]), text("Outline"), @@ -1154,18 +1272,24 @@ impl Window for ElementStyle { ColorPicker::PressedOutline, self.id ), - checkbox("Show Outline", style.pressed.show_outline), + checkbox("Show Outline", style.pressed.show_outline).on_toggle(move |_| { + Message::ChangeStyle(StyleSetting::PressedKeyShowOutline(id)) + }), row![ - number_input(style.pressed.outline_width, 1.., |_| Message::none()), + number_input(style.pressed.outline_width, 1.., move |v| { + Message::ChangeStyle(StyleSetting::PressedKeyOutlineWidth { + id, + width: v, + }) + }), text(" Outline Width"), ] ]), - checkbox("Overwrite default style", true) ]; row![column_1, column_2].into() } - _ => todo!(), + style::ElementStyle::MouseSpeedIndicatorStyle(_) => text("Temp").into(), } } } diff --git a/crates/types/src/config.rs b/crates/types/src/config.rs index b2fe04b..9fdd8ff 100644 --- a/crates/types/src/config.rs +++ b/crates/types/src/config.rs @@ -3,17 +3,6 @@ use ordered_float::OrderedFloat; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -pub fn get_id(element: &BoardElement) -> u32 { - if let Ok(def) = CommonDefinitionRef::try_from(element) { - *def.id - } else { - let BoardElement::MouseSpeedIndicator(def) = element else { - unreachable!() - }; - def.id - } -} - #[derive(Serialize, Deserialize, Default, Debug, JsonSchema)] #[serde(rename_all = "PascalCase")] pub struct Layout { @@ -37,6 +26,15 @@ pub enum BoardElement { } impl BoardElement { + pub fn id(&self) -> u32 { + match self { + BoardElement::KeyboardKey(key) => key.id, + BoardElement::MouseKey(key) => key.id, + BoardElement::MouseScroll(key) => key.id, + BoardElement::MouseSpeedIndicator(key) => key.id, + } + } + pub fn translate(&mut self, delta: geo::Coord, move_text: bool) { match self { BoardElement::MouseSpeedIndicator(key) => {