use crate::color_scheme::ColorScheme; use anyhow::Result; use gtk::glib; use gtk::prelude::*; use gtk::{ Box as GtkBox, ColorDialog, ColorDialogButton, InfoBar, Label, MessageType, Orientation, Picture, ScrolledWindow, }; use std::cell::RefCell; use std::path::Path; use std::rc::Rc; pub fn build_editor_page(icon_path: &Path, on_back: impl Fn() + 'static) -> Result { let scheme = ColorScheme::load(icon_path)?; let scheme = Rc::new(RefCell::new(scheme)); let root = GtkBox::new(Orientation::Vertical, 8); root.set_margin_top(8); root.set_margin_bottom(8); root.set_margin_start(8); root.set_margin_end(8); let toolbar = GtkBox::new(Orientation::Horizontal, 8); let back_button = gtk::Button::with_label("← Choose another icon"); back_button.connect_clicked(move |_| on_back()); toolbar.append(&back_button); let undo_button = gtk::Button::with_label("Undo"); let redo_button = gtk::Button::with_label("Redo"); toolbar.append(&undo_button); toolbar.append(&redo_button); root.append(&toolbar); if scheme.borrow().colors.is_empty() { let info = InfoBar::new(); info.set_message_type(MessageType::Warning); info.add_child(&Label::new(Some( "No ColorScheme tags were found in this SVG.", ))); root.append(&info); } let content = GtkBox::new(Orientation::Horizontal, 8); content.set_vexpand(true); let swatches_scroller = ScrolledWindow::new(); swatches_scroller.set_min_content_width(300); let swatches_box = GtkBox::new(Orientation::Vertical, 4); swatches_scroller.set_child(Some(&swatches_box)); content.append(&swatches_scroller); let preview = Picture::new(); preview.set_hexpand(true); content.append(&preview); root.append(&content); let refresh_preview = { let scheme = Rc::clone(&scheme); let preview = preview.clone(); move || { let bytes = glib::Bytes::from_owned(scheme.borrow().content.clone().into_bytes()); match gtk::gdk::Texture::from_bytes(&bytes) { Ok(texture) => preview.set_paintable(Some(&texture)), Err(_) => preview.set_paintable(gtk::gdk::Paintable::NONE), } } }; refresh_preview(); let rebuild_swatches: Rc>> = Rc::new(RefCell::new(Box::new(|| {}))); { let scheme = Rc::clone(&scheme); let swatches_box = swatches_box.clone(); let refresh_preview = refresh_preview.clone(); let rebuild_swatches_for_closure = Rc::clone(&rebuild_swatches); let build_fn = move || { while let Some(child) = swatches_box.first_child() { swatches_box.remove(&child); } let tags: Vec<(String, String)> = scheme.borrow().colors.clone(); for (tag, hex) in tags { let row = GtkBox::new(Orientation::Horizontal, 8); let label = Label::new(Some(&tag)); label.set_hexpand(true); label.set_xalign(0.0); let rgba = gtk::gdk::RGBA::parse(&hex).unwrap_or(gtk::gdk::RGBA::BLACK); let dialog = ColorDialog::builder().with_alpha(false).build(); let button = ColorDialogButton::new(Some(dialog)); button.set_rgba(&rgba); { let scheme = Rc::clone(&scheme); let tag = tag.clone(); let refresh_preview = refresh_preview.clone(); let rebuild_swatches_for_closure = Rc::clone(&rebuild_swatches_for_closure); button.connect_rgba_notify(move |btn| { let new_rgba = btn.rgba(); let new_hex = format!( "#{:02x}{:02x}{:02x}", (new_rgba.red() * 255.0).round() as u8, (new_rgba.green() * 255.0).round() as u8, (new_rgba.blue() * 255.0).round() as u8, ); let changed = scheme .borrow_mut() .update_color(&tag, &new_hex) .unwrap_or(false); if changed { refresh_preview(); (rebuild_swatches_for_closure.borrow())(); } }); } row.append(&label); row.append(&button); swatches_box.append(&row); } }; *rebuild_swatches.borrow_mut() = Box::new(build_fn); } (rebuild_swatches.borrow())(); { let scheme = Rc::clone(&scheme); let refresh_preview = refresh_preview.clone(); let rebuild_swatches = Rc::clone(&rebuild_swatches); undo_button.connect_clicked(move |_| { if scheme.borrow_mut().undo().unwrap_or(false) { refresh_preview(); (rebuild_swatches.borrow())(); } }); } { let scheme = Rc::clone(&scheme); let refresh_preview = refresh_preview.clone(); let rebuild_swatches = Rc::clone(&rebuild_swatches); redo_button.connect_clicked(move |_| { if scheme.borrow_mut().redo().unwrap_or(false) { refresh_preview(); (rebuild_swatches.borrow())(); } }); } Ok(root.upcast()) }