aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dialogs.rs189
-rw-r--r--src/main.rs25
-rw-r--r--src/preferences/pages/editor.rs43
-rw-r--r--src/preferences/pages/general.rs58
-rw-r--r--src/preferences/pages/map.rs26
5 files changed, 156 insertions, 185 deletions
diff --git a/src/dialogs.rs b/src/dialogs.rs
index 41b8c86..242f3d6 100644
--- a/src/dialogs.rs
+++ b/src/dialogs.rs
@@ -19,6 +19,7 @@
use std::path::PathBuf;
use gtk::prelude::*;
+use relm4::gtk::gio;
use relm4::{ComponentSender, gtk};
use crate::AppModel;
@@ -38,18 +39,13 @@ pub fn create_file_filter() -> gtk::FileFilter {
/// Shows an error dialog with the given title and message.
pub fn show_error(window: &gtk::Window, title: &str, message: &str) {
- let dialog = gtk::MessageDialog::new(
- Some(window),
- gtk::DialogFlags::MODAL | gtk::DialogFlags::DESTROY_WITH_PARENT,
- gtk::MessageType::Error,
- gtk::ButtonsType::Ok,
- message,
- );
- dialog.set_title(Some(title));
- dialog.connect_response(|dialog, _| {
- dialog.close();
- });
- dialog.show();
+ let dialog = gtk::AlertDialog::builder()
+ .modal(true)
+ .buttons([&tr!("dialog.action.cancel"), "Ok"])
+ .message(title)
+ .detail(message)
+ .build();
+ dialog.show(Some(window));
}
/// Shows a file open dialog and handles the response.
@@ -58,21 +54,19 @@ pub fn show_open_dialog(
is_empty_document: bool,
sender: ComponentSender<AppModel>,
) {
- let dialog = gtk::FileChooserDialog::new(
- Some(&tr!("dialog.open.title")),
- Some(window),
- gtk::FileChooserAction::Open,
- &[
- (&tr!("dialog.action.cancel"), gtk::ResponseType::Cancel),
- (&tr!("dialog.action.open"), gtk::ResponseType::Accept),
- ],
- );
- dialog.add_filter(&create_file_filter());
+ let dialog = gtk::FileDialog::builder()
+ .title(tr!("dialog.open.title"))
+ .modal(true)
+ .build();
- dialog.connect_response(move |dialog, response| {
- dialog.close();
- if response == gtk::ResponseType::Accept
- && let Some(file) = dialog.file()
+ let filter = create_file_filter();
+ let filters = gtk::gio::ListStore::new::<gtk::FileFilter>();
+ filters.append(&filter);
+ dialog.set_filters(Some(&filters));
+
+ let window_clone = window.clone();
+ gtk::glib::spawn_future_local(async move {
+ if let Ok(file) = dialog.open_future(Some(&window_clone)).await
&& let Some(path) = file.path()
{
// Check if file is already open - if so, focus that window
@@ -88,7 +82,6 @@ pub fn show_open_dialog(
}
}
});
- dialog.present();
}
/// Shows a file save dialog and handles the response.
@@ -98,40 +91,38 @@ pub fn show_save_dialog(
close_after: bool,
sender: ComponentSender<AppModel>,
) {
- let dialog = gtk::FileChooserDialog::new(
- Some(&tr!("dialog.save.title")),
- Some(window),
- gtk::FileChooserAction::Save,
- &[
- (&tr!("dialog.action.cancel"), gtk::ResponseType::Cancel),
- (&tr!("dialog.action.save"), gtk::ResponseType::Accept),
- ],
- );
- dialog.add_filter(&create_file_filter());
+ let mut dialog_builder = gtk::FileDialog::builder()
+ .title(tr!("dialog.save.title"))
+ .modal(true);
+
+ let filter = create_file_filter();
+ let filters = gtk::gio::ListStore::new::<gtk::FileFilter>();
+ filters.append(&filter);
+ dialog_builder = dialog_builder.filters(&filters);
let default_filename = "map.wmap";
if let Some(current_path) = current_file {
if let Some(parent) = current_path.parent() {
let folder = gtk::gio::File::for_path(parent);
- dialog.set_current_folder(Some(&folder)).ok();
+ dialog_builder = dialog_builder.initial_folder(&folder);
}
if let Some(filename) = current_path.file_name() {
- dialog.set_current_name(filename.to_str().unwrap_or(default_filename));
+ dialog_builder =
+ dialog_builder.initial_name(filename.to_str().unwrap_or(default_filename));
}
} else {
- dialog.set_current_name(default_filename);
+ dialog_builder = dialog_builder.initial_name(default_filename);
}
- dialog.connect_response(move |dialog, response| {
- dialog.close();
- if response == gtk::ResponseType::Accept
- && let Some(file) = dialog.file()
+ let dialog = dialog_builder.build();
+ let window_clone = window.clone();
+ gtk::glib::spawn_future_local(async move {
+ if let Ok(file) = dialog.save_future(Some(&window_clone)).await
&& let Some(path) = file.path()
{
sender.input(Action::SaveToFile { path, close_after });
}
});
- dialog.present();
}
/// Shows a dialog asking whether to save unsaved changes before closing.
@@ -140,39 +131,42 @@ pub fn show_unsaved_changes_dialog(
current_file: Option<PathBuf>,
sender: ComponentSender<AppModel>,
) {
- let dialog = gtk::MessageDialog::new(
- Some(window),
- gtk::DialogFlags::MODAL | gtk::DialogFlags::DESTROY_WITH_PARENT,
- gtk::MessageType::Warning,
- gtk::ButtonsType::None,
- tr!("dialog.unsaved_changes.message"),
- );
- dialog.set_title(Some(&tr!("dialog.unsaved_changes.title")));
- dialog.add_button(&tr!("dialog.action.dont_save"), gtk::ResponseType::Reject);
- dialog.add_button(&tr!("dialog.action.cancel"), gtk::ResponseType::Cancel);
- dialog.add_button(&tr!("dialog.action.save"), gtk::ResponseType::Accept);
+ let dialog = gtk::AlertDialog::builder()
+ .modal(true)
+ .message(tr!("dialog.unsaved_changes.title"))
+ .detail(tr!("dialog.unsaved_changes.message"))
+ .buttons([
+ tr!("dialog.action.dont_save"),
+ tr!("dialog.action.cancel"),
+ tr!("dialog.action.save"),
+ ])
+ .cancel_button(1)
+ .default_button(2)
+ .build();
- let window = window.clone();
- dialog.connect_response(move |dialog, response| {
- dialog.close();
- match response {
- gtk::ResponseType::Accept => {
+ //dialog.show(Some(window));
+ let cancellable = gio::Cancellable::current();
+ let window_clone = window.clone();
+ dialog.choose(
+ Some(window),
+ cancellable.as_ref(),
+ move |answer| match answer {
+ Ok(0) => {
+ if let Some(ref path) = current_file {
+ file_registry::unregister_file(path);
+ }
+ window_clone.destroy();
+ }
+ Ok(2) => {
if current_file.is_some() {
sender.input(Action::Save { close_after: true });
} else {
sender.input(Action::SaveAs { close_after: true });
}
}
- gtk::ResponseType::Reject => {
- if let Some(ref path) = current_file {
- file_registry::unregister_file(path);
- }
- window.destroy();
- }
_ => {}
- }
- });
- dialog.show();
+ },
+ );
}
/// Creates a file filter for an image format.
@@ -194,20 +188,18 @@ pub fn show_export_dialog(
current_file: Option<&PathBuf>,
sender: ComponentSender<AppModel>,
) {
- let dialog = gtk::FileChooserDialog::new(
- Some(&tr!("dialog.export.title")),
- Some(window),
- gtk::FileChooserAction::Save,
- &[
- (&tr!("dialog.action.cancel"), gtk::ResponseType::Cancel),
- (&tr!("dialog.action.export"), gtk::ResponseType::Accept),
- ],
- );
-
let png_filter = create_image_filter(ImageFormat::Png);
let svg_filter = create_image_filter(ImageFormat::Svg);
- dialog.add_filter(&png_filter);
- dialog.add_filter(&svg_filter);
+
+ let filters = gtk::gio::ListStore::new::<gtk::FileFilter>();
+ filters.append(&png_filter);
+ filters.append(&svg_filter);
+
+ let mut dialog_builder = gtk::FileDialog::builder()
+ .title(tr!("dialog.export.title"))
+ .modal(true)
+ .filters(&filters)
+ .default_filter(&png_filter);
// Set default filename based on current file
let base_name = current_file
@@ -221,35 +213,19 @@ pub fn show_export_dialog(
&& let Some(parent) = current_path.parent()
{
let folder = gtk::gio::File::for_path(parent);
- dialog.set_current_folder(Some(&folder)).ok();
+ dialog_builder = dialog_builder.initial_folder(&folder);
}
- dialog.set_current_name(&format!("{base_name}.png"));
- // Update extension when filter changes
- {
- let png_filter_ref = png_filter.clone();
- let base_name_clone = base_name.clone();
- dialog.connect_notify_local(Some("filter"), move |dialog, _| {
- let ext = if dialog.filter().as_ref() == Some(&png_filter_ref) {
- "png"
- } else {
- "svg"
- };
- dialog.set_current_name(&format!("{base_name_clone}.{ext}"));
- });
- }
+ dialog_builder = dialog_builder.initial_name(format!("{base_name}.png"));
- let png_filter_clone = png_filter.clone();
- dialog.connect_response(move |dialog, response| {
- dialog.close();
- if response == gtk::ResponseType::Accept
- && let Some(file) = dialog.file()
+ let dialog = dialog_builder.build();
+ let window_clone = window.clone();
+ gtk::glib::spawn_future_local(async move {
+ if let Ok(file) = dialog.save_future(Some(&window_clone)).await
&& let Some(path) = file.path()
{
- // Determine format from selected filter or file extension
- let format = if dialog.filter().as_ref() == Some(&png_filter_clone) {
- ImageFormat::Png
- } else if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
+ // Determine format from file extension
+ let format = if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
match ext.to_lowercase().as_str() {
"svg" => ImageFormat::Svg,
_ => ImageFormat::Png,
@@ -268,5 +244,4 @@ pub fn show_export_dialog(
sender.input(Action::ExportToFile { path, format });
}
});
- dialog.present();
}
diff --git a/src/main.rs b/src/main.rs
index 5aff910..41d3f68 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -43,7 +43,6 @@ use crate::icon_names::shipped::{
use cairo::ImageSurface;
use gtk::gio;
use gtk::glib;
-use gtk::prelude::*;
use relm4::{
Component, ComponentController, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt,
SimpleComponent,
@@ -51,7 +50,7 @@ use relm4::{
gtk,
};
use sourceview5::LanguageManager;
-use sourceview5::prelude::{BufferExt, TextBufferExt, TextViewExt};
+use sourceview5::prelude::*;
use stages::{ALL_STAGE_TYPES, LocalizedStageType};
use wmap_parser::{Map, parse};
use wmap_renderer::{Configuration, StageType, render_to_surface};
@@ -289,18 +288,18 @@ impl AppModel {
let css = if self.preferences.use_custom_editor_font {
self.source_view.set_monospace(false);
format!(
- "textview {{ font-family: \"{}\"; font-size: {}pt; }}",
+ ".map-editor {{ font-family: \"{}\"; font-size: {}pt; }}",
self.preferences.custom_editor_font_name, self.preferences.editor_font_size as i32
)
} else {
self.source_view.set_monospace(true);
format!(
- "textview {{ font-size: {}pt; }}",
+ ".map-editor {{ font-size: {}pt; }}",
self.preferences.editor_font_size as i32
)
};
- self.editor_css_provider.load_from_data(&css);
+ self.editor_css_provider.load_from_string(&css);
self.render_configuration.options.smart_label_positioning =
self.preferences.use_smart_label_positioning;
@@ -630,10 +629,14 @@ impl SimpleComponent for AppModel {
source_view.set_monospace(true);
let editor_css_provider = gtk::CssProvider::new();
- source_view.style_context().add_provider(
- &editor_css_provider,
- gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
- );
+ source_view.add_css_class("map-editor");
+ if let Some(display) = gtk::gdk::Display::default() {
+ gtk::style_context_add_provider_for_display(
+ &display,
+ &editor_css_provider,
+ gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
+ );
+ }
let (drawing_area, map, render_configuration, stage_type, surface) =
Self::init_drawing_area(&initial_content);
@@ -755,7 +758,9 @@ impl SimpleComponent for AppModel {
Action::SaveToFile { path, close_after } => {
handlers::file::save_to_file(self, path, close_after);
}
- Action::CloseWindow => handlers::file::close_window(self, &sender),
+ Action::CloseWindow => {
+ handlers::file::close_window(self, &sender);
+ }
Action::MarkInitialized => self.initialized = true,
diff --git a/src/preferences/pages/editor.rs b/src/preferences/pages/editor.rs
index 27b94da..c0e9b41 100644
--- a/src/preferences/pages/editor.rs
+++ b/src/preferences/pages/editor.rs
@@ -31,7 +31,7 @@ pub struct EditorPage {
font_size_scale: gtk::Scale,
soft_wrap_switch: gtk::Switch,
custom_font_switch: gtk::Switch,
- font_button: gtk::FontButton,
+ font_button: gtk::FontDialogButton,
}
impl EditorPage {
@@ -124,21 +124,23 @@ impl EditorPage {
(soft_wrap_row.1, custom_font_row.1)
}
- fn create_font_button(preferences: &UserPreferences) -> gtk::FontButton {
- let button = gtk::FontButton::new();
+ fn create_font_button(preferences: &UserPreferences) -> gtk::FontDialogButton {
+ let font_dialog = gtk::FontDialog::new();
+ let font_button = gtk::FontDialogButton::new(Some(font_dialog));
#[allow(clippy::cast_possible_truncation)]
- button.set_font(&format!(
+ let font_desc = gtk::pango::FontDescription::from_string(&format!(
"{} {}",
preferences.custom_editor_font_name, preferences.editor_font_size as i32
));
- button.set_sensitive(preferences.use_custom_editor_font);
- button.set_hexpand(true);
- button.set_use_font(true);
- button.set_use_size(false);
- button
+ font_button.set_font_desc(&font_desc);
+ font_button.set_sensitive(preferences.use_custom_editor_font);
+ font_button.set_hexpand(true);
+ font_button.set_use_font(true);
+ font_button.set_use_size(false);
+ font_button
}
- fn create_font_row(font_button: &gtk::FontButton) -> gtk::Box {
+ fn create_font_row(font_button: &gtk::FontDialogButton) -> gtk::Box {
let row = gtk::Box::new(gtk::Orientation::Horizontal, 10);
row.append(&gtk::Label::new(Some(&tr!("preferences.editor.font"))));
row.append(font_button);
@@ -150,7 +152,7 @@ impl EditorPage {
font_size_scale: &gtk::Scale,
soft_wrap_switch: &gtk::Switch,
custom_font_switch: &gtk::Switch,
- font_button: &gtk::FontButton,
+ font_button: &gtk::FontDialogButton,
) {
let s = sender.clone();
let fb = font_button.clone();
@@ -167,15 +169,14 @@ impl EditorPage {
let s = sender.clone();
let scale = font_size_scale.clone();
- font_button.connect_font_set(move |button| {
- if let Some(font) = button.font() {
- let font_desc = gtk::pango::FontDescription::from_string(&font);
- if let Some(family) = font_desc.family() {
+ font_button.connect_font_desc_notify(move |button| {
+ if let Some(font) = button.font_desc() {
+ if let Some(family) = font.family() {
s.emit(PreferencesInput::SetCustomEditorFontName(
family.to_string(),
));
}
- let size = font_desc.size() / gtk::pango::SCALE;
+ let size = font.size() / gtk::pango::SCALE;
if size > 0 {
scale.set_value(f64::from(size));
}
@@ -191,12 +192,11 @@ impl EditorPage {
});
}
- fn update_font_button_size(font_button: &gtk::FontButton, size: f64) {
- if let Some(font) = font_button.font() {
- let mut font_desc = gtk::pango::FontDescription::from_string(&font);
+ fn update_font_button_size(font_button: &gtk::FontDialogButton, size: f64) {
+ if let Some(mut font_desc) = font_button.font_desc() {
#[allow(clippy::cast_possible_truncation)]
font_desc.set_size(size as i32 * gtk::pango::SCALE);
- font_button.set_font(&font_desc.to_string());
+ font_button.set_font_desc(&font_desc);
}
}
@@ -213,9 +213,10 @@ impl EditorPage {
self.font_button
.set_sensitive(preferences.use_custom_editor_font);
#[allow(clippy::cast_possible_truncation)]
- self.font_button.set_font(&format!(
+ let font_desc = gtk::pango::FontDescription::from_string(&format!(
"{} {}",
preferences.custom_editor_font_name, preferences.editor_font_size as i32
));
+ self.font_button.set_font_desc(&font_desc);
}
}
diff --git a/src/preferences/pages/general.rs b/src/preferences/pages/general.rs
index ad15c82..d1596c4 100644
--- a/src/preferences/pages/general.rs
+++ b/src/preferences/pages/general.rs
@@ -97,31 +97,25 @@ pub fn show_export_dialog(sender: &Sender<PreferencesInput>, window: &gtk::Windo
filter.add_pattern("*.json");
filter.set_name(Some(&tr!("dialog.file_type.json")));
- let dialog = gtk::FileChooserDialog::new(
- Some(&tr!("dialog.export_preferences.title")),
- Some(window),
- gtk::FileChooserAction::Save,
- &[
- (&tr!("dialog.action.cancel"), gtk::ResponseType::Cancel),
- (&tr!("dialog.action.save"), gtk::ResponseType::Accept),
- ],
- );
- dialog.set_current_name("preferences.json");
- dialog.add_filter(&filter);
- dialog.set_modal(true);
+ let filters = gtk::gio::ListStore::new::<gtk::FileFilter>();
+ filters.append(&filter);
+
+ let dialog = gtk::FileDialog::builder()
+ .title(tr!("dialog.export_preferences.title"))
+ .modal(true)
+ .filters(&filters)
+ .initial_name("preferences.json")
+ .build();
let sender = sender.clone();
- dialog.connect_response(move |dialog, response| {
- if response == gtk::ResponseType::Accept
- && let Some(file) = dialog.file()
+ let window = window.clone();
+ gtk::glib::spawn_future_local(async move {
+ if let Ok(file) = dialog.save_future(Some(&window)).await
&& let Some(path) = file.path()
{
sender.emit(PreferencesInput::ExportToFile(path));
}
- dialog.close();
});
-
- dialog.present();
}
pub fn show_import_dialog(sender: &Sender<PreferencesInput>, window: &gtk::Window) {
@@ -129,28 +123,22 @@ pub fn show_import_dialog(sender: &Sender<PreferencesInput>, window: &gtk::Windo
filter.add_pattern("*.json");
filter.set_name(Some(&tr!("dialog.file_type.json")));
- let dialog = gtk::FileChooserDialog::new(
- Some(&tr!("dialog.import_preferences.title")),
- Some(window),
- gtk::FileChooserAction::Open,
- &[
- (&tr!("dialog.action.cancel"), gtk::ResponseType::Cancel),
- (&tr!("dialog.action.open"), gtk::ResponseType::Accept),
- ],
- );
- dialog.add_filter(&filter);
- dialog.set_modal(true);
+ let filters = gtk::gio::ListStore::new::<gtk::FileFilter>();
+ filters.append(&filter);
+
+ let dialog = gtk::FileDialog::builder()
+ .title(tr!("dialog.import_preferences.title"))
+ .modal(true)
+ .filters(&filters)
+ .build();
let sender = sender.clone();
- dialog.connect_response(move |dialog, response| {
- if response == gtk::ResponseType::Accept
- && let Some(file) = dialog.file()
+ let window = window.clone();
+ gtk::glib::spawn_future_local(async move {
+ if let Ok(file) = dialog.open_future(Some(&window)).await
&& let Some(path) = file.path()
{
sender.emit(PreferencesInput::ImportFromFile(path));
}
- dialog.close();
});
-
- dialog.present();
}
diff --git a/src/preferences/pages/map.rs b/src/preferences/pages/map.rs
index 4355c6f..5645a59 100644
--- a/src/preferences/pages/map.rs
+++ b/src/preferences/pages/map.rs
@@ -14,9 +14,9 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-use gtk::prelude::*;
use relm4::Sender;
use relm4::gtk;
+use sourceview5::prelude::*;
use crate::tr;
use crate::ui_helpers::create_switch_row;
@@ -29,7 +29,7 @@ pub struct MapPage {
show_background_switch: gtk::Switch,
smart_positioning_switch: gtk::Switch,
custom_font_switch: gtk::Switch,
- font_button: gtk::FontButton,
+ font_button: gtk::FontDialogButton,
export_format_dropdown: gtk::DropDown,
}
@@ -61,7 +61,7 @@ impl MapPage {
container: &gtk::Box,
preferences: &UserPreferences,
sender: &Sender<PreferencesInput>,
- ) -> (gtk::Switch, gtk::Switch, gtk::Switch, gtk::FontButton) {
+ ) -> (gtk::Switch, gtk::Switch, gtk::Switch, gtk::FontDialogButton) {
let style_label = gtk::Label::new(Some(&tr!("preferences.map.map_style.title")));
style_label.add_css_class("heading");
style_label.set_halign(gtk::Align::Start);
@@ -109,19 +109,20 @@ impl MapPage {
let font_row = gtk::Box::new(gtk::Orientation::Horizontal, 10);
font_row.append(&gtk::Label::new(Some(&tr!("preferences.map.font"))));
- let font_button = gtk::FontButton::new();
- font_button.set_font(&preferences.custom_font_name);
+ let font_dialog = gtk::FontDialog::new();
+ let font_button = gtk::FontDialogButton::new(Some(font_dialog));
+ let font_desc = gtk::pango::FontDescription::from_string(&preferences.custom_font_name);
+ font_button.set_font_desc(&font_desc);
font_button.set_sensitive(preferences.use_custom_font);
font_button.set_hexpand(true);
font_button.set_use_font(true);
{
let sender = sender.clone();
- font_button.connect_font_set(move |button| {
- if let Some(font) = button.font() {
- let font_desc = gtk::pango::FontDescription::from_string(&font);
- if let Some(family) = font_desc.family() {
- sender.emit(PreferencesInput::SetCustomFontName(family.to_string()));
- }
+ font_button.connect_font_desc_notify(move |button| {
+ if let Some(font) = button.font_desc()
+ && let Some(family) = font.family()
+ {
+ sender.emit(PreferencesInput::SetCustomFontName(family.to_string()));
}
});
}
@@ -199,7 +200,8 @@ impl MapPage {
self.custom_font_switch
.set_active(preferences.use_custom_font);
self.font_button.set_sensitive(preferences.use_custom_font);
- self.font_button.set_font(&preferences.custom_font_name);
+ let font_desc = gtk::pango::FontDescription::from_string(&preferences.custom_font_name);
+ self.font_button.set_font_desc(&font_desc);
let selected = match preferences.default_export_format.as_str() {
"svg" => 1,