summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main.cpp1
-rw-r--r--lang/include/turns/lang/messages.hpp5
-rw-r--r--lang/po/de.po15
-rw-r--r--lang/po/en.po15
-rw-r--r--lang/tests/messages.cpp7
-rw-r--r--ui/include/turns/ui/windows/tracker.hpp16
-rw-r--r--ui/res/ui.cmb40
-rw-r--r--ui/src/windows/tracker.cpp64
8 files changed, 140 insertions, 23 deletions
diff --git a/app/src/main.cpp b/app/src/main.cpp
index 119022f..abecf27 100644
--- a/app/src/main.cpp
+++ b/app/src/main.cpp
@@ -32,6 +32,7 @@ auto main(int argc, char * argv[]) -> int
app->set_accel_for_action("win.next", "<Primary>space");
app->set_accel_for_action("win.previous", "<Primary>BackSpace");
app->set_accel_for_action("win.add_participant", "<Primary>a");
+ app->set_accel_for_action("win.save", "<Primary>s");
});
app->signal_shutdown().connect([window] {
diff --git a/lang/include/turns/lang/messages.hpp b/lang/include/turns/lang/messages.hpp
index 59a099b..b709d9d 100644
--- a/lang/include/turns/lang/messages.hpp
+++ b/lang/include/turns/lang/messages.hpp
@@ -13,15 +13,20 @@ namespace turns::lang
auto constexpr static main_menu = "Main Menu";
auto constexpr static mark_as_defeated = "Mark as defeated";
auto constexpr static name = "Name";
+ auto constexpr static new_turn_order_file_name = "New turn order.trns";
auto constexpr static next_participant = "Next participant";
auto constexpr static no_active_turn_order = "No active turn order";
auto constexpr static previous_participant = "Previous participant";
auto constexpr static priority = "Priority";
auto constexpr static priority_number = "Priority {}";
auto constexpr static quit = "_Quit";
+ auto constexpr static save = "_Save";
+ auto constexpr static saving_failed_format = "Saving failed: {}";
auto constexpr static round_number = "Round {}";
auto constexpr static start_turn_order = "Start turn order";
+ auto constexpr static successfully_saved_format = "Successfully saved '{}'";
auto constexpr static turns = "Turns";
+ auto constexpr static turns_files = "Turns Files";
} // namespace turns::lang
#endif \ No newline at end of file
diff --git a/lang/po/de.po b/lang/po/de.po
index 698d813..5880c5a 100644
--- a/lang/po/de.po
+++ b/lang/po/de.po
@@ -38,6 +38,9 @@ msgstr "Als besiegt markieren"
msgid "Name"
msgstr "Name"
+msgid "New turn order.trns"
+msgstr "Neue Zugreihenfolge.trns"
+
msgid "Next participant"
msgstr "Nächster Teilnehmer"
@@ -59,8 +62,20 @@ msgstr "_Beenden"
msgid "Round {}"
msgstr "Runde {}"
+msgid "_Save"
+msgstr "_Speichern"
+
+msgid "Saving failed: {}"
+msgstr "Speichern fehlgeschlagen: {}"
+
msgid "Start turn order"
msgstr "Zugreihenfolge starten"
+msgid "Successfully saved '{}'"
+msgstr "'{}' wurde erfolgreich gespeichert."
+
msgid "Turns"
msgstr "Züge"
+
+msgid "Turns Files"
+msgstr "Züge Dateien" \ No newline at end of file
diff --git a/lang/po/en.po b/lang/po/en.po
index b716a17..76f970d 100644
--- a/lang/po/en.po
+++ b/lang/po/en.po
@@ -38,6 +38,9 @@ msgstr "Mark as defeated"
msgid "Name"
msgstr "Name"
+msgid "New turn order.trns"
+msgstr "New turn order.trns"
+
msgid "Next participant"
msgstr "Next participant"
@@ -59,8 +62,20 @@ msgstr "_Quit"
msgid "Round {}"
msgstr "Round {}"
+msgid "_Save"
+msgstr "_Save"
+
+msgid "Saving failed: {}"
+msgstr "Saving failed: {}"
+
msgid "Start turn order"
msgstr "Start turn order"
+msgid "Successfully saved '{}'"
+msgstr "Successfully saved '{}'."
+
msgid "Turns"
msgstr "Turns"
+
+msgid "Turns Files"
+msgstr "Turns Files"
diff --git a/lang/tests/messages.cpp b/lang/tests/messages.cpp
index 33c4202..7f474d3 100644
--- a/lang/tests/messages.cpp
+++ b/lang/tests/messages.cpp
@@ -29,15 +29,20 @@ namespace turns::lang::tests
mark_as_defeated,
// a better solution is required to test the following entry:
// name,
+ new_turn_order_file_name,
next_participant,
no_active_turn_order,
previous_participant,
priority,
priority_number,
quit,
+ save,
+ saving_failed_format,
start_turn_order,
+ successfully_saved_format,
round_number,
- turns);
+ turns,
+ turns_files);
SECTION(std::format("has a translation for '{}'", message))
{
diff --git a/ui/include/turns/ui/windows/tracker.hpp b/ui/include/turns/ui/windows/tracker.hpp
index b1af178..a5fe833 100644
--- a/ui/include/turns/ui/windows/tracker.hpp
+++ b/ui/include/turns/ui/windows/tracker.hpp
@@ -1,22 +1,28 @@
#ifndef TURNS_UI_WINDOWS_TRACKER_HPP
#define TURNS_UI_WINDOWS_TRACKER_HPP
-#include "turns/ui/widgets/turn_order_view.hpp"
#include "turns/core/turn_order.hpp"
+#include "turns/ui/widgets/turn_order_view.hpp"
#include <glibmm/refptr.h>
#include <glibmm/ustring.h>
#include <glibmm/variant.h>
+#include <giomm/asyncresult.h>
+
#include <gtkmm/applicationwindow.h>
#include <gtkmm/builder.h>
#include <gtkmm/button.h>
+#include <gtkmm/filedialog.h>
#include <gtkmm/revealer.h>
#include <gtkmm/stack.h>
#include <gtkmm/widget.h>
#include <adwaita.h>
+#include <string>
+#include <optional>
+
namespace turns::ui::windows
{
@@ -28,6 +34,8 @@ namespace turns::ui::windows
auto handle_add_participant() -> void;
auto handle_delete_participant(Glib::VariantBase param) -> void;
auto handle_edit_participant(Glib::VariantBase param) -> void;
+ auto handle_save() -> void;
+ auto handle_save_finish(Glib::RefPtr<Gio::AsyncResult> result, Glib::RefPtr<Gtk::FileDialog> dialog) -> void;
auto handle_stop() -> void;
auto setup_actions() -> void;
@@ -37,14 +45,18 @@ namespace turns::ui::windows
AdwApplicationWindow * m_adw;
Gtk::Revealer * m_controls;
Gtk::Widget * m_empty;
+ AdwToastOverlay * m_overlay;
Gtk::Stack * m_stack;
Gtk::Button * m_start;
AdwWindowTitle * m_title;
Glib::RefPtr<core::turn_order> m_turn_order;
widgets::turn_order_view * m_turn_order_view;
Glib::PropertyProxy<Glib::ustring> m_subtitle;
+
+ std::string m_file_tag{};
+ std::optional<std::string> m_file_name{};
};
-} // namespace turns::app::windows
+} // namespace turns::ui::windows
#endif \ No newline at end of file
diff --git a/ui/res/ui.cmb b/ui/res/ui.cmb
index acf1687..58005a6 100644
--- a/ui/res/ui.cmb
+++ b/ui/res/ui.cmb
@@ -24,10 +24,7 @@
(1,7,"GtkMenuButton","open_main_menu",3,None,"end",None,None,None,None),
(1,8,"GtkButton","add_participant",3,None,"start",None,1,None,None),
(1,9,"(menu)","main_menu",None,None,None,None,-1,None,None),
- (1,10,"(item)",None,9,None,None,None,1,None,None),
- (1,11,"GtkStack","stack",2,None,None,None,-1,None,None),
- (1,16,"AdwStatusPage","empty",11,None,None,None,-1,None,None),
- (1,17,"GtkButton",None,16,None,None,None,-1,None,None),
+ (1,10,"(item)",None,9,None,None,None,2,None,None),
(1,18,"GtkButton","start",3,None,"start",None,2,None,None),
(1,19,"(item)",None,9,None,None,None,None,None,None),
(1,20,"GtkRevealer","controls",2,None,"bottom",None,-1,None,None),
@@ -35,6 +32,11 @@
(1,22,"GtkButton",None,21,None,"start",None,-1,None,None),
(1,23,"GtkButton",None,21,None,"center",None,-1,None,None),
(1,24,"GtkButton",None,21,None,"end",None,-1,None,None),
+ (1,25,"(item)",None,9,None,None,None,1,None,None),
+ (1,26,"AdwToastOverlay","overlay",2,None,None,None,-1,None,None),
+ (1,30,"GtkStack","stack",26,None,None,None,-1,None,None),
+ (1,31,"AdwStatusPage","empty",30,None,None,None,None,None,None),
+ (1,32,"GtkButton",None,31,None,None,None,None,None,None),
(2,1,"AdwDialog","participant_editor",None,None,None,None,-1,None,None),
(2,2,"AdwToolbarView",None,1,None,None,None,-1,None,None),
(2,3,"AdwHeaderBar",None,2,None,"top",None,-1,None,None),
@@ -68,7 +70,7 @@
(1,1,"GtkWidget","width-request","360",None,None,None,None,None,None,None,None,None),
(1,1,"GtkWindow","default-height","720",None,None,None,None,None,None,None,None,None),
(1,1,"GtkWindow","default-width","360",None,None,None,None,None,None,None,None,None),
- (1,2,"AdwToolbarView","content",None,None,None,None,None,11,None,None,None,None),
+ (1,2,"AdwToolbarView","content",None,None,None,None,None,26,None,None,None,None),
(1,3,"AdwHeaderBar","title-widget",None,None,None,None,None,6,None,None,None,None),
(1,6,"AdwWindowTitle","subtitle","No active turn order",1,None,None,None,None,None,None,None,None),
(1,6,"AdwWindowTitle","title","Turns",1,None,None,None,None,None,None,None,None),
@@ -80,12 +82,6 @@
(1,8,"GtkWidget","tooltip-text","Add participant",1,None,None,None,None,None,None,None,None),
(1,10,"(item)","action","app.quit",None,None,None,None,None,None,None,None,None),
(1,10,"(item)","label","_Quit",1,None,None,None,None,None,None,None,None),
- (1,11,"GtkStack","transition-type","crossfade",None,None,None,None,None,None,None,None,None),
- (1,16,"AdwStatusPage","child",None,None,None,None,None,17,None,None,None,None),
- (1,16,"AdwStatusPage","icon-name","contact-new-symbolic",None,None,None,None,None,None,None,None,None),
- (1,17,"GtkActionable","action-name","win.add_participant",None,None,None,None,None,None,None,None,None),
- (1,17,"GtkButton","label","Add participant",1,None,None,None,None,None,None,None,None),
- (1,17,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(1,18,"GtkActionable","action-name","win.start",None,None,None,None,None,None,None,None,None),
(1,18,"GtkButton","icon-name","media-playback-start-symbolic",None,None,None,None,None,None,None,None,None),
(1,18,"GtkWidget","tooltip-text","Start turn order",1,None,None,None,None,None,None,None,None),
@@ -102,6 +98,14 @@
(1,24,"GtkActionable","action-name","win.next",None,None,None,None,None,None,None,None,None),
(1,24,"GtkButton","icon-name","media-skip-forward-symbolic",None,None,None,None,None,None,None,None,None),
(1,24,"GtkWidget","tooltip-markup","Next participant",1,None,None,None,None,None,None,None,None),
+ (1,25,"(item)","action","win.save",None,None,None,None,None,None,None,None,None),
+ (1,25,"(item)","label","_Save",1,None,None,None,None,None,None,None,None),
+ (1,26,"AdwToastOverlay","child",None,None,None,None,None,30,None,None,None,None),
+ (1,31,"AdwStatusPage","child",None,None,None,None,None,32,None,None,None,None),
+ (1,31,"AdwStatusPage","icon-name","contact-new-symbolic",None,None,None,None,None,None,None,None,None),
+ (1,32,"GtkActionable","action-name","win.add_participant",None,None,None,None,None,None,None,None,None),
+ (1,32,"GtkButton","label","Add participant",1,None,None,None,None,None,None,None,None),
+ (1,32,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(2,1,"AdwDialog","child",None,None,None,None,None,2,None,None,None,None),
(2,1,"AdwDialog","default-widget",None,None,None,None,None,None,None,None,None,None),
(2,1,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
@@ -165,9 +169,6 @@
(2,9,"GtkWidget",1,1,None,None,None,None,None,None),
(2,9,"GtkWidget",2,2,None,1,None,None,None,None),
(2,9,"GtkWidget",2,3,None,1,None,None,None,None),
- (1,17,"GtkWidget",1,1,None,None,None,None,None,None),
- (1,17,"GtkWidget",2,2,None,1,None,None,None,None),
- (1,17,"GtkWidget",2,3,None,1,None,None,None,None),
(3,6,"GtkWidget",1,1,None,None,None,None,None,None),
(3,6,"GtkWidget",2,2,None,1,None,None,None,None),
(3,2,"GtkWidget",1,1,None,None,None,None,None,None),
@@ -200,15 +201,16 @@
(4,2,"GtkWidget",1,1,None,None,None,None,None,None),
(4,2,"GtkWidget",2,2,None,1,None,None,None,None),
(4,5,"GtkWidget",1,1,None,None,None,None,None,None),
- (4,5,"GtkWidget",2,2,None,1,None,None,None,None)
+ (4,5,"GtkWidget",2,2,None,1,None,None,None,None),
+ (1,32,"GtkWidget",1,1,None,None,None,None,None,None),
+ (1,32,"GtkWidget",2,2,None,1,None,None,None,None),
+ (1,32,"GtkWidget",2,3,None,1,None,None,None,None)
</object_data>
<object_data_arg>
(1,1,"GtkWidget",2,2,"name","background"),
(2,6,"GtkWidget",2,2,"name","boxed-list"),
(2,9,"GtkWidget",2,2,"name","pill"),
(2,9,"GtkWidget",2,3,"name","suggested-action"),
- (1,17,"GtkWidget",2,2,"name","pill"),
- (1,17,"GtkWidget",2,3,"name","suggested-action"),
(3,6,"GtkWidget",2,2,"name","circular"),
(3,2,"GtkWidget",2,2,"name","header"),
(3,7,"GtkWidget",2,2,"name","title"),
@@ -227,6 +229,8 @@
(1,24,"GtkWidget",2,3,"name","suggested-action"),
(1,21,"GtkWidget",2,2,"name","toolbar"),
(4,2,"GtkWidget",2,2,"name","osd"),
- (4,5,"GtkWidget",2,2,"name","boxed-list")
+ (4,5,"GtkWidget",2,2,"name","boxed-list"),
+ (1,32,"GtkWidget",2,2,"name","pill"),
+ (1,32,"GtkWidget",2,3,"name","suggested-action")
</object_data_arg>
</cambalache-project>
diff --git a/ui/src/windows/tracker.cpp b/ui/src/windows/tracker.cpp
index 8add81f..5571aca 100644
--- a/ui/src/windows/tracker.cpp
+++ b/ui/src/windows/tracker.cpp
@@ -5,12 +5,19 @@
#include "turns/lang/messages.hpp"
#include "turns/ui/windows/participant_editor.hpp"
+#include <sigc++/bind.h>
#include <sigc++/functors/mem_fun.h>
#include <glibmm/binding.h>
#include <glibmm/i18n.h>
+#include <giomm/liststore.h>
+
+#include <gtkmm/error.h>
+#include <gtkmm/filedialog.h>
+
#include <adwaita.h>
+#include <nlohmann/json.hpp>
#include <format>
#include <utility>
@@ -43,6 +50,7 @@ namespace turns::ui::windows
, m_adw{ADW_APPLICATION_WINDOW(gobj())}
, m_controls{builder->get_widget<Gtk::Revealer>("controls")}
, m_empty(builder->get_widget<Gtk::Widget>("empty"))
+ , m_overlay{ADW_TOAST_OVERLAY(builder->get_widget<Gtk::Widget>("overlay")->gobj())}
, m_stack{builder->get_widget<Gtk::Stack>("stack")}
, m_start{builder->get_widget<Gtk::Button>("start")}
, m_title(ADW_WINDOW_TITLE(builder->get_widget<Gtk::Widget>("title")->gobj()))
@@ -92,6 +100,48 @@ namespace turns::ui::windows
dialog->present(this);
}
+ auto tracker::handle_save() -> void
+ {
+ auto filters = Gio::ListStore<Gtk::FileFilter>::create();
+ auto filter = Gtk::FileFilter::create();
+ filter->set_name(_("Turns Files"));
+ filter->add_pattern("*.trns");
+ filters->append(filter);
+
+ auto dialog = Gtk::FileDialog::create();
+ dialog->set_initial_name(m_file_name.value_or(_(lang::new_turn_order_file_name)));
+ dialog->set_filters(filters);
+ dialog->save(*this, sigc::bind(sigc::mem_fun(*this, &tracker::handle_save_finish), dialog));
+ }
+
+ auto tracker::handle_save_finish(Glib::RefPtr<Gio::AsyncResult> result, Glib::RefPtr<Gtk::FileDialog> dialog) -> void
+ try
+ {
+ auto file = dialog->save_finish(result);
+ file->replace_contents(m_turn_order->serialize().dump(2), m_file_tag, m_file_tag);
+ auto name = file->get_basename();
+ auto message = std::vformat(_(lang::successfully_saved_format), std::make_format_args(name));
+ auto toast = adw_toast_new(message.c_str());
+ adw_toast_overlay_add_toast(m_overlay, toast);
+ }
+ catch (Gtk::DialogError const & e)
+ {
+ if (e.code() == Gtk::DialogError::FAILED)
+ {
+ auto error = e.what();
+ auto message = std::vformat(_(lang::saving_failed_format), std::make_format_args(error));
+ auto toast = adw_toast_new(message.c_str());
+ adw_toast_overlay_add_toast(m_overlay, toast);
+ }
+ }
+ catch (Gio::Error const & e)
+ {
+ auto error = e.what();
+ auto message = std::vformat(_(lang::saving_failed_format), std::make_format_args(error));
+ auto toast = adw_toast_new(message.c_str());
+ adw_toast_overlay_add_toast(m_overlay, toast);
+ }
+
auto tracker::handle_stop() -> void
{
auto dialog = ADW_ALERT_DIALOG(adw_alert_dialog_new("Stop turn order", "Do you want to clear the turn order?"));
@@ -174,6 +224,16 @@ namespace turns::ui::windows
add_action_with_parameter("delete", Glib::VARIANT_TYPE_INT32, sigc::mem_fun(*this, &tracker::handle_delete_participant));
add_action_with_parameter("edit", Glib::VARIANT_TYPE_INT32, sigc::mem_fun(*this, &tracker::handle_edit_participant));
}
+
+ // win.save
+ // depends-on: turn_order:is_empty == false
+ {
+ auto action = add_action("save", sigc::mem_fun(*this, &tracker::handle_save));
+
+ Glib::Binding::bind_property(m_turn_order->is_empty(),
+ action->property_enabled(),
+ Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN);
+ }
}
auto tracker::update_subtitle() -> void
@@ -185,8 +245,8 @@ namespace turns::ui::windows
else
{
auto round_number = m_turn_order->round_number() + 1;
- m_subtitle = round_number == 0 ? "" : std::vformat(_("Round {}"), std::make_format_args(round_number));
+ m_subtitle = round_number == 0 ? "" : std::vformat(_(lang::round_number), std::make_format_args(round_number));
}
}
-} // namespace turns::app::windows
+} // namespace turns::ui::windows