diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/actions.rs | 4 | ||||
| -rw-r--r-- | src/dialogs.rs | 12 | ||||
| -rw-r--r-- | src/file_registry.rs | 8 | ||||
| -rw-r--r-- | src/main.rs | 246 | ||||
| -rw-r--r-- | src/preferences/pages/general.rs | 6 | ||||
| -rw-r--r-- | src/preferences/window.rs | 6 |
6 files changed, 159 insertions, 123 deletions
diff --git a/src/actions.rs b/src/actions.rs index fdea125..1d60820 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -60,6 +60,10 @@ pub enum Action { MarkInitialized, CloseWindow, + // Layout Transition + DisableHorizontalLayout, + EnableHorizontalLayout, + // Preferences ShowPreferences, PreferencesChanged(UserPreferences), diff --git a/src/dialogs.rs b/src/dialogs.rs index 242f3d6..22b12df 100644 --- a/src/dialogs.rs +++ b/src/dialogs.rs @@ -20,7 +20,7 @@ use std::path::PathBuf; use gtk::prelude::*; use relm4::gtk::gio; -use relm4::{ComponentSender, gtk}; +use relm4::{ComponentSender, adw, gtk}; use crate::AppModel; use crate::actions::{Action, ImageFormat}; @@ -38,7 +38,7 @@ pub fn create_file_filter() -> gtk::FileFilter { } /// Shows an error dialog with the given title and message. -pub fn show_error(window: >k::Window, title: &str, message: &str) { +pub fn show_error(window: &adw::Window, title: &str, message: &str) { let dialog = gtk::AlertDialog::builder() .modal(true) .buttons([&tr!("dialog.action.cancel"), "Ok"]) @@ -50,7 +50,7 @@ pub fn show_error(window: >k::Window, title: &str, message: &str) { /// Shows a file open dialog and handles the response. pub fn show_open_dialog( - window: >k::Window, + window: &adw::Window, is_empty_document: bool, sender: ComponentSender<AppModel>, ) { @@ -86,7 +86,7 @@ pub fn show_open_dialog( /// Shows a file save dialog and handles the response. pub fn show_save_dialog( - window: >k::Window, + window: &adw::Window, current_file: Option<&PathBuf>, close_after: bool, sender: ComponentSender<AppModel>, @@ -127,7 +127,7 @@ pub fn show_save_dialog( /// Shows a dialog asking whether to save unsaved changes before closing. pub fn show_unsaved_changes_dialog( - window: >k::Window, + window: &adw::Window, current_file: Option<PathBuf>, sender: ComponentSender<AppModel>, ) { @@ -184,7 +184,7 @@ fn create_image_filter(format: ImageFormat) -> gtk::FileFilter { /// Shows an export image dialog and handles the response. pub fn show_export_dialog( - window: >k::Window, + window: &adw::Window, current_file: Option<&PathBuf>, sender: ComponentSender<AppModel>, ) { diff --git a/src/file_registry.rs b/src/file_registry.rs index 749697d..188e1fe 100644 --- a/src/file_registry.rs +++ b/src/file_registry.rs @@ -21,7 +21,7 @@ use std::cell::RefCell; use std::path::PathBuf; use relm4::component::Controller; -use relm4::gtk; +use relm4::{adw}; use crate::AppModel; use crate::actions::Action; @@ -31,7 +31,7 @@ thread_local! { static WINDOW_CONTROLLERS: RefCell<Vec<Controller<AppModel>>> = const { RefCell::new(Vec::new()) }; /// Tracks which files are currently open to prevent duplicate windows. - static OPEN_FILES: RefCell<Vec<(PathBuf, gtk::Window)>> = const { RefCell::new(Vec::new()) }; + static OPEN_FILES: RefCell<Vec<(PathBuf, adw::Window)>> = const { RefCell::new(Vec::new()) }; /// Stores senders for all windows (including main window) to enable broadcasting. static WINDOW_SENDERS: RefCell<Vec<relm4::Sender<Action>>> = const { RefCell::new(Vec::new()) }; @@ -45,7 +45,7 @@ pub fn store_controller(controller: Controller<AppModel>) { } /// Returns the window that has the given file open, if any. -pub fn find_window_for_file(path: &PathBuf) -> Option<gtk::Window> { +pub fn find_window_for_file(path: &PathBuf) -> Option<adw::Window> { OPEN_FILES.with_borrow(|files| { files .iter() @@ -55,7 +55,7 @@ pub fn find_window_for_file(path: &PathBuf) -> Option<gtk::Window> { } /// Registers a file as being open in the given window. -pub fn register_file(path: &PathBuf, window: >k::Window) { +pub fn register_file(path: &PathBuf, window: &adw::Window) { OPEN_FILES.with_borrow_mut(|files| { if !files.iter().any(|(file_path, _)| file_path == path) { files.push((path.clone(), window.clone())); diff --git a/src/main.rs b/src/main.rs index 41d3f68..e1d3bc9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,8 +47,9 @@ use relm4::{ Component, ComponentController, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent, actions::{AccelsPlus, ActionablePlus, RelmAction, RelmActionGroup}, - gtk, + gtk, adw }; +use adw::prelude::*; use sourceview5::LanguageManager; use sourceview5::prelude::*; use stages::{ALL_STAGE_TYPES, LocalizedStageType}; @@ -88,105 +89,12 @@ macro_rules! register_action { let action: RelmAction<$action_type> = RelmAction::new_stateless(move |_| { sender.input($message); }); - $group.add_action(action); + $group.add_action(action.clone()); $app.set_accelerators_for_action::<$action_type>($accelerators); + action }}; } -fn setup_actions(window: >k::Window, sender: &ComponentSender<AppModel>) { - let application = relm4::main_application(); - let mut action_group = RelmActionGroup::<WindowActionGroup>::new(); - - register_action!( - action_group, - application, - sender, - ZoomInAction, - Action::ZoomIn, - &["<Primary>equal", "<Primary>plus"] - ); - register_action!( - action_group, - application, - sender, - ZoomOutAction, - Action::ZoomOut, - &["<Primary>minus"] - ); - register_action!( - action_group, - application, - sender, - ChangeOrientationAction, - Action::ChangeOrientation, - &["<Primary>l"] - ); - register_action!( - action_group, - application, - sender, - ExportImageAction, - Action::ExportImage, - &["<Primary>e"] - ); - register_action!( - action_group, - application, - sender, - NewAction, - Action::New, - &["<Primary>n"] - ); - register_action!( - action_group, - application, - sender, - SaveAction, - Action::Save { close_after: false }, - &["<Primary>s"] - ); - register_action!( - action_group, - application, - sender, - SaveAsAction, - Action::SaveAs { close_after: false }, - &["<Primary><Shift>s"] - ); - register_action!( - action_group, - application, - sender, - OpenAction, - Action::Open, - &["<Primary>o"] - ); - register_action!( - action_group, - application, - sender, - CloseAction, - Action::CloseWindow, - &["<Primary>w"] - ); - register_action!( - action_group, - application, - sender, - PreferencesAction, - Action::ShowPreferences, - &["<Primary>comma"] - ); - - let sender = sender.clone(); - let action: RelmAction<NewFromTemplateAction> = - RelmAction::new_with_target_value(move |_, template_id| { - sender.input(Action::NewFromTemplateById(template_id)); - }); - action_group.add_action(action); - action_group.register_for_widget(window); -} - struct AppInit { zoom: f64, file_path: Option<PathBuf>, @@ -211,12 +119,14 @@ struct AppModel { modified: bool, pending_load_events: RefCell<u32>, initialized: bool, - window: gtk::Window, + window: adw::Window, preferences: preferences::UserPreferences, preferences_window: Option<relm4::Controller<preferences::PreferencesWindow>>, editor_css_provider: gtk::CssProvider, templates_menu: gio::Menu, main_menu: gio::Menu, + horizontal_layout_enabled: bool, + layout_action: RelmAction<ChangeOrientationAction> } impl AppModel { @@ -487,7 +397,7 @@ impl SimpleComponent for AppModel { view! { #[root] - gtk::Window { + adw::Window { #[watch] set_title: Some(&model.window_title()), set_default_width: constants::DEFAULT_WINDOW_WIDTH, @@ -497,10 +407,8 @@ impl SimpleComponent for AppModel { set_orientation: gtk::Orientation::Vertical, set_spacing: 0, - gtk::HeaderBar { - pack_start = >k::Box { - set_orientation: gtk::Orientation::Horizontal, - + adw::HeaderBar { + pack_start = &adw::ToolbarView { gtk::Button::with_label(&match model.orientation { gtk::Orientation::Horizontal => tr!("command.view.use_vertical_layout"), _ => tr!("command.view.use_horizontal_layout") @@ -510,11 +418,12 @@ impl SimpleComponent for AppModel { gtk::Orientation::Horizontal => SPLIT_VERTICAL_REGULAR, _ => SPLIT_HORIZONTAL_REGULAR }, + #[watch] + set_visible: model.horizontal_layout_enabled, ActionablePlus::set_stateless_action::<ChangeOrientationAction>: &(), } }, - pack_end = >k::Box { - set_orientation: gtk::Orientation::Horizontal, + pack_end = &adw::ToolbarView { gtk::Button::with_label(&tr!("command.file.export")) { set_icon_name: IMAGE_REGULAR, @@ -540,7 +449,7 @@ impl SimpleComponent for AppModel { gtk::Paned { #[watch] - set_orientation: model.orientation, + set_orientation: if model.horizontal_layout_enabled { model.orientation } else { gtk::Orientation::Vertical }, set_expand: true, #[wrap(Some)] @@ -623,8 +532,6 @@ impl SimpleComponent for AppModel { let (source, initial_content, current_file) = Self::init_source_buffer(&init, &sender); - setup_actions(&root, &sender); - let source_view = sourceview5::View::with_buffer(&source); source_view.set_monospace(true); @@ -648,6 +555,100 @@ impl SimpleComponent for AppModel { let prefs = preferences::storage::load(); let (templates_menu, main_menu) = Self::init_main_menu(&prefs); + // Actions + + let application = relm4::main_application(); + let mut action_group = RelmActionGroup::<WindowActionGroup>::new(); + + register_action!( + action_group, + application, + sender, + ZoomInAction, + Action::ZoomIn, + &["<Primary>equal", "<Primary>plus"] + ); + register_action!( + action_group, + application, + sender, + ZoomOutAction, + Action::ZoomOut, + &["<Primary>minus"] + ); + let layout_action = register_action!( + action_group, + application, + sender, + ChangeOrientationAction, + Action::ChangeOrientation, + &["<Primary>l"] + ); + register_action!( + action_group, + application, + sender, + ExportImageAction, + Action::ExportImage, + &["<Primary>e"] + ); + register_action!( + action_group, + application, + sender, + NewAction, + Action::New, + &["<Primary>n"] + ); + register_action!( + action_group, + application, + sender, + SaveAction, + Action::Save { close_after: false }, + &["<Primary>s"] + ); + register_action!( + action_group, + application, + sender, + SaveAsAction, + Action::SaveAs { close_after: false }, + &["<Primary><Shift>s"] + ); + register_action!( + action_group, + application, + sender, + OpenAction, + Action::Open, + &["<Primary>o"] + ); + register_action!( + action_group, + application, + sender, + CloseAction, + Action::CloseWindow, + &["<Primary>w"] + ); + register_action!( + action_group, + application, + sender, + PreferencesAction, + Action::ShowPreferences, + &["<Primary>comma"] + ); + + let sender_clone = sender.clone(); + let action: RelmAction<NewFromTemplateAction> = + RelmAction::new_with_target_value(move |_, template_id| { + sender_clone.input(Action::NewFromTemplateById(template_id)); + }); + action_group.add_action(action); + action_group.register_for_widget(&root); + let mut model = AppModel { orientation: gtk::Orientation::Horizontal, stage_type_list, @@ -672,6 +673,8 @@ impl SimpleComponent for AppModel { editor_css_provider, templates_menu, main_menu, + horizontal_layout_enabled: true, + layout_action }; model.apply_preferences(); @@ -697,6 +700,23 @@ impl SimpleComponent for AppModel { let widgets = view_output!(); + // Breakpoints for Smaller Layouts + let breakpoint = adw::Breakpoint::new( + adw::BreakpointCondition::new_length( + adw::BreakpointConditionLengthType::MinWidth, + 440.0, + adw::LengthUnit::Sp + ) + ); + let sender_clone = sender.clone(); + breakpoint.connect_apply(move |_| { + sender_clone.input(Action::EnableHorizontalLayout); + }); + let sender_clone = sender.clone(); + breakpoint.connect_unapply(move |_| { + sender_clone.input(Action::DisableHorizontalLayout); + }); + root.add_breakpoint(breakpoint); ComponentParts { model, widgets } } @@ -764,6 +784,18 @@ impl SimpleComponent for AppModel { Action::MarkInitialized => self.initialized = true, + // Layout Transition + Action::DisableHorizontalLayout => { + self.horizontal_layout_enabled = false; + self.layout_action.set_enabled(false); + }, + Action::EnableHorizontalLayout => { + self.horizontal_layout_enabled = true; + self.layout_action.set_enabled(true); + }, + + // Preferences + Action::ShowPreferences => { if self.preferences_window.is_none() { let controller = preferences::PreferencesWindow::builder() diff --git a/src/preferences/pages/general.rs b/src/preferences/pages/general.rs index d1596c4..e65ede6 100644 --- a/src/preferences/pages/general.rs +++ b/src/preferences/pages/general.rs @@ -17,7 +17,7 @@ use gtk::FileFilter; use gtk::prelude::*; use relm4::Sender; -use relm4::gtk; +use relm4::{adw, gtk}; use crate::tr; @@ -92,7 +92,7 @@ impl GeneralPage { } } -pub fn show_export_dialog(sender: &Sender<PreferencesInput>, window: >k::Window) { +pub fn show_export_dialog(sender: &Sender<PreferencesInput>, window: &adw::Window) { let filter = FileFilter::new(); filter.add_pattern("*.json"); filter.set_name(Some(&tr!("dialog.file_type.json"))); @@ -118,7 +118,7 @@ pub fn show_export_dialog(sender: &Sender<PreferencesInput>, window: >k::Windo }); } -pub fn show_import_dialog(sender: &Sender<PreferencesInput>, window: >k::Window) { +pub fn show_import_dialog(sender: &Sender<PreferencesInput>, window: &adw::Window) { let filter = FileFilter::new(); filter.add_pattern("*.json"); filter.set_name(Some(&tr!("dialog.file_type.json"))); diff --git a/src/preferences/window.rs b/src/preferences/window.rs index 0997716..b7db8c5 100644 --- a/src/preferences/window.rs +++ b/src/preferences/window.rs @@ -20,7 +20,7 @@ use std::path::PathBuf; use gtk::prelude::*; -use relm4::{ComponentParts, ComponentSender, SimpleComponent, gtk}; +use relm4::{ComponentParts, ComponentSender, SimpleComponent, adw, gtk}; use crate::tr; @@ -85,7 +85,7 @@ pub enum PreferencesOutput { pub struct PreferencesWindow { preferences: UserPreferences, - window: gtk::Window, + window: adw::Window, // Widget references for pages general_page: general::GeneralPage, editor_page: editor::EditorPage, @@ -101,7 +101,7 @@ impl SimpleComponent for PreferencesWindow { type Output = PreferencesOutput; view! { - gtk::Window { + adw::Window { set_title: Some(&tr!("preferences.title")), set_default_width: 700, set_default_height: 500, |