diff --git a/src/app.rs b/src/app.rs index 98fd7e5..a2a5ee4 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1485,6 +1485,7 @@ pub fn run_with( ui_state.set_eraser_radius_mm(ui_settings.eraser_radius_mm); ui_state.math.set_enabled(ui_settings.math_assist); ui_state.touch_draw_enabled = ui_settings.touch_draw; + ui_state.saved_colors = ui_settings.saved_colors.clone(); ui_state.select_pen(ui_settings.active_pen); ui_state.highlighter_colors = ui_settings.highlighter_colors; ui_state.select_highlighter_color(ui_settings.active_highlighter); @@ -5636,6 +5637,7 @@ pub fn run_with( updated.text_colors = ui_state.text_colors; updated.active_text_color = ui_state.active_text_color; updated.eraser_radius_mm = ui_state.eraser_radius_mm; + updated.saved_colors.clone_from(&ui_state.saved_colors); if updated != ui_settings { ui_settings = updated; save_ui_settings( diff --git a/src/i18n/en.rs b/src/i18n/en.rs index 32a65f9..6be6859 100644 --- a/src/i18n/en.rs +++ b/src/i18n/en.rs @@ -108,6 +108,10 @@ pub const EN: &[(&str, &str)] = &[ ("Dunkelgrau", "Dark gray"), ("Dunkle Stiftfarben aufhellen", "Lighten dark pen colors"), ("Eigene Farbe wählen", "Choose a custom color"), + ("Meine Farbe", "My color"), + ("➕ Farbe merken", "➕ Save color"), + ("Diese Farbe aus „Meine Farben“ entfernen", "Remove this color from “My colors”"), + ("Die aktuelle Farbe zu „Meine Farben“ hinzufügen", "Add the current color to “My colors”"), ("Ein Ordner kann nicht in sich selbst verschoben werden", "A folder cannot be moved into itself"), ("Ein-/Ausgabefehler: {e}", "I/O error: {e}"), ("Eine Fläche ohne Rand: in alle Richtungen schreiben, verschieben und zoomen. Wechsel nur bei einer einzigen Seite ohne PDF bzw. zurück, solange alles im Startausschnitt liegt.", "One surface without edges: write, pan and zoom in every direction. Switching only works for a single page without a PDF, and back only while everything lies in the starting area."), diff --git a/src/settings.rs b/src/settings.rs index 4b35e40..10de737 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -217,6 +217,9 @@ pub struct StylusButton { /// Höchstens so viele Stifttasten werden gemerkt. pub const MAX_STYLUS_BUTTONS: usize = 4; +/// How many own colors fit under the presets of the color popovers (two rows) +pub const MAX_SAVED_COLORS: usize = 12; + /// Benutzereinstellungen der Oberfläche. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(default)] @@ -257,6 +260,8 @@ pub struct UiSettings { /// One finger draws instead of moving the page. On by default on Android, where phones /// usually have no pen (Issue #50) pub touch_draw: bool, + /// Own colors under the presets of the color popovers (Issue #56) + pub saved_colors: Vec<[f32; 3]>, } impl Default for UiSettings { @@ -286,6 +291,7 @@ impl Default for UiSettings { storage_dir: None, math_assist: false, touch_draw: cfg!(target_os = "android"), + saved_colors: Vec::new(), } } } @@ -333,6 +339,11 @@ impl UiSettings { .active_text_color .min(settings.text_colors.len() - 1); settings.stylus_buttons.truncate(MAX_STYLUS_BUTTONS); + settings.saved_colors.truncate(MAX_SAVED_COLORS); + for rgb in &mut settings.saved_colors { + let [r, g, b, _] = sanitized_color([rgb[0], rgb[1], rgb[2], 1.0], [0.0, 0.0, 0.0, 1.0]); + *rgb = [r, g, b]; + } settings } @@ -412,6 +423,7 @@ mod tests { storage_dir: Some(PathBuf::from("/home/pia/Nextcloud/ink-notes")), math_assist: true, touch_draw: true, + saved_colors: vec![[0.2, 0.4, 0.6], [1.0, 0.5, 0.0]], }; settings.save_to(&path).unwrap(); assert_eq!(UiSettings::load_from(&path), settings); diff --git a/src/ui.rs b/src/ui.rs index 3529618..1b80fa8 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -20,7 +20,7 @@ use crate::file_picker::{FilePicker, PickerKind}; use crate::ruler::{Ruler, RulerHit}; use crate::settings::{ PenPreset, PressureCurve, SidebarSide, DEFAULT_HIGHLIGHTER_COLORS, DEFAULT_PEN_PRESETS, - DEFAULT_TEXT_COLORS, PEN_PRESET_COUNT, + DEFAULT_TEXT_COLORS, MAX_SAVED_COLORS, PEN_PRESET_COUNT, }; use crate::storage::{NotebookLayout, NotebookSummary}; use crate::text::{TextEditing, TextFont, TEXT_SIZES_PT}; @@ -446,6 +446,8 @@ pub struct UiState { pub is_text_color_menu_open: bool, /// Farbwähler in den Farb-Popovern aufgeklappt pub show_color_picker: bool, + /// „Meine Farben“ in the color popovers (Issue #56) + pub saved_colors: Vec<[f32; 3]>, /// Kontextmenü nach langem Drücken (Einfügen an dieser Stelle) pub context_menu: Option, /// Laufendes Platzschaffen (Vorschau) @@ -575,6 +577,7 @@ impl Default for UiState { active_text_color: 0, is_text_color_menu_open: false, show_color_picker: false, + saved_colors: Vec::new(), context_menu: None, space_drag: None, stylus_buttons: Vec::new(), @@ -1304,17 +1307,21 @@ fn autosave_status_style(status: AutosaveStatus) -> (egui::Color32, &'static str fn color_editor( ui: &mut egui::Ui, presets: &[ColorPreset], + saved: &mut Vec<[f32; 3]>, color: &mut [f32; 4], picker_open: &mut bool, ) -> bool { + let same_rgb = |a: &[f32], b: &[f32]| { + a[..3] + .iter() + .zip(&b[..3]) + .all(|(a, b)| (a - b).abs() < 0.01) + }; let mut changed = false; ui.horizontal(|ui| { ui.spacing_mut().item_spacing = egui::vec2(8.0, 0.0); for &(preset, label) in presets { - let is_sel = preset[..3] - .iter() - .zip(&color[..3]) - .all(|(a, b)| (a - b).abs() < 0.01); + let is_sel = same_rgb(&preset, color); if color_circle_swatch(ui, preset, is_sel, crate::i18n::tr(label), 30.0).clicked() { *color = [preset[0], preset[1], preset[2], color[3]]; changed = true; @@ -1331,6 +1338,46 @@ fn color_editor( *picker_open = !*picker_open; } }); + + // My colors (Issue #56): kept for pen, highlighter and text alike + ui.add_space(6.0); + ui.horizontal_wrapped(|ui| { + ui.spacing_mut().item_spacing = egui::vec2(8.0, 6.0); + let mut remove = None; + for (i, rgb) in saved.iter().enumerate() { + let swatch = [rgb[0], rgb[1], rgb[2], 1.0]; + let is_sel = same_rgb(rgb, color); + if color_circle_swatch(ui, swatch, is_sel, t!("Meine Farbe"), 30.0).clicked() { + *color = [rgb[0], rgb[1], rgb[2], color[3]]; + changed = true; + } + if is_sel { + remove = Some(i); + } + } + if let Some(i) = remove { + if ui + .button(egui::RichText::new("🗑").size(16.0)) + .on_hover_text(t!("Diese Farbe aus „Meine Farben“ entfernen")) + .clicked() + { + saved.remove(i); + } + } else if saved.len() < MAX_SAVED_COLORS { + let label = if saved.is_empty() { + t!("➕ Farbe merken") + } else { + "➕" + }; + if ui + .button(label) + .on_hover_text(t!("Die aktuelle Farbe zu „Meine Farben“ hinzufügen")) + .clicked() + { + saved.push([color[0], color[1], color[2]]); + } + } + }); if *picker_open { ui.add_space(6.0); let [r, g, b] = [0, 1, 2].map(|i| (color[i].clamp(0.0, 1.0) * 255.0).round() as u8); @@ -4093,6 +4140,7 @@ pub fn render_ui( if color_editor( ui, &PEN_COLOR_PRESETS, + &mut state.saved_colors, &mut state.pen_color, &mut state.show_color_picker, ) { @@ -4274,6 +4322,7 @@ pub fn render_ui( if color_editor( ui, &HIGHLIGHTER_COLOR_PRESETS, + &mut state.saved_colors, &mut state.highlighter_color, &mut state.show_color_picker, ) { @@ -4380,6 +4429,7 @@ pub fn render_ui( if color_editor( ui, &PEN_COLOR_PRESETS, + &mut state.saved_colors, &mut color, &mut state.show_color_picker, ) {