summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/include/turns/app/widgets/turn_order_view.hpp7
-rw-r--r--app/include/turns/app/windows/tracker.hpp10
-rw-r--r--app/src/application.cpp5
-rw-r--r--app/src/widgets/turn_order_view.cpp15
-rw-r--r--app/src/windows/tracker.cpp102
-rw-r--r--domain/include/turns/domain/turn_order.hpp47
-rw-r--r--domain/src/turn_order.cpp142
-rw-r--r--res/ui.cmb4
8 files changed, 287 insertions, 45 deletions
diff --git a/app/include/turns/app/widgets/turn_order_view.hpp b/app/include/turns/app/widgets/turn_order_view.hpp
index 0122f4a..022b51c 100644
--- a/app/include/turns/app/widgets/turn_order_view.hpp
+++ b/app/include/turns/app/widgets/turn_order_view.hpp
@@ -2,19 +2,19 @@
#define TURNS_APP_WIDGETS_TURN_ORDER_VIEW_HPP
#include "turns/app/widgets/template_widget.hpp"
-#include "turns/domain/turn_order.hpp"
-#include "turns/domain/participant.hpp"
#include "turns/domain/disposition.hpp"
+#include "turns/domain/participant.hpp"
+#include "turns/domain/turn_order.hpp"
#include <array>
#include <glibmm/object.h>
#include <glibmm/propertyproxy.h>
#include <glibmm/refptr.h>
+#include <glibmm/ustring.h>
#include <gtkmm/listbox.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/widget.h>
-#include <glibmm/ustring.h>
namespace turns::app::widgets
{
@@ -29,6 +29,7 @@ namespace turns::app::widgets
auto get_model() const noexcept -> Glib::RefPtr<domain::turn_order>;
private:
+ auto handle_active_participant_changed() -> void;
auto handle_create_row(Glib::RefPtr<Glib::Object> const item) -> Gtk::Widget *;
Glib::RefPtr<domain::turn_order> m_model;
diff --git a/app/include/turns/app/windows/tracker.hpp b/app/include/turns/app/windows/tracker.hpp
index b9b45a7..292d6d5 100644
--- a/app/include/turns/app/windows/tracker.hpp
+++ b/app/include/turns/app/windows/tracker.hpp
@@ -1,8 +1,8 @@
#ifndef TURNS_APP_WINDOWS_TRACKER_HPP
#define TURNS_APP_WINDOWS_TRACKER_HPP
-#include "turns/domain/turn_order.hpp"
#include "turns/app/widgets/turn_order_view.hpp"
+#include "turns/domain/turn_order.hpp"
#include <adwaita.h>
#include <giomm/simpleaction.h>
@@ -13,11 +13,12 @@
#include <glibmm/variant.h>
#include <gtkmm/applicationwindow.h>
#include <gtkmm/builder.h>
+#include <gtkmm/button.h>
#include <gtkmm/listbox.h>
+#include <gtkmm/revealer.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/stack.h>
#include <gtkmm/widget.h>
-#include <gtkmm/revealer.h>
namespace turns::app::windows
{
@@ -30,11 +31,16 @@ namespace turns::app::windows
auto handle_add_participant() -> void;
auto handle_delete_participant(Glib::VariantBase param) -> void;
auto handle_edit_participant(Glib::VariantBase param) -> void;
+ auto handle_start() -> void;
+ auto handle_stop() -> void;
+
+ auto setup_actions() -> void;
AdwApplicationWindow * m_adw;
Gtk::Revealer * m_controls;
Gtk::Widget * m_empty;
Gtk::Stack * m_stack;
+ Gtk::Button * m_start;
AdwWindowTitle * m_title;
widgets::turn_order_view * m_turn_order;
Glib::PropertyProxy<Glib::ustring> m_subtitle;
diff --git a/app/src/application.cpp b/app/src/application.cpp
index efa1ce0..bf49a6e 100644
--- a/app/src/application.cpp
+++ b/app/src/application.cpp
@@ -61,8 +61,11 @@ namespace turns::app
adw_style_manager_set_color_scheme(style_manager, ADW_COLOR_SCHEME_PREFER_LIGHT);
m_application->add_action("quit", sigc::mem_fun(*this, &application::handle_action_quit));
- m_application->set_accel_for_action("app.quit", "<ctrl>q");
+ m_application->set_accel_for_action("app.quit", "<Primary>q");
m_application->set_accel_for_action("win.clear", "<Primary>x");
+ m_application->set_accel_for_action("win.next", "<Primary>space");
+ m_application->set_accel_for_action("win.previous", "<Primary>BackSpace");
+ m_application->set_accel_for_action("win.add_participant", "<Primary>a");
register_derived_widgets();
}
diff --git a/app/src/widgets/turn_order_view.cpp b/app/src/widgets/turn_order_view.cpp
index 700dfd3..c2b4afa 100644
--- a/app/src/widgets/turn_order_view.cpp
+++ b/app/src/widgets/turn_order_view.cpp
@@ -4,6 +4,7 @@
#include "turns/domain/participant.hpp"
#include "turns/lang/messages.hpp"
+#include <algorithm>
#include <format>
#include <sigc++/functors/mem_fun.h>
@@ -23,6 +24,7 @@ namespace turns::app::widgets
, m_view{get_widget<Gtk::ListBox>("view")}
{
m_view->bind_model(m_model->list_model(), sigc::mem_fun(*this, &turn_order_view::handle_create_row));
+ m_model->property_active_participant().signal_changed().connect(sigc::mem_fun(*this, &turn_order_view::handle_active_participant_changed));
}
auto turn_order_view::get_model() const noexcept -> Glib::RefPtr<domain::turn_order>
@@ -30,6 +32,19 @@ namespace turns::app::widgets
return m_model;
}
+ auto turn_order_view::handle_active_participant_changed() -> void
+ {
+ std::ranges::for_each(m_view->get_children(), [](auto c) { c->remove_css_class("active-participant"); });
+
+ auto index = m_model->active_participant();
+ if (index != std::numeric_limits<domain::turn_order::active_participant_type>::max())
+ {
+ auto row = m_view->get_row_at_index(index);
+ row->add_css_class("active-participant");
+ row->grab_focus();
+ }
+ }
+
auto turn_order_view::handle_create_row(Glib::RefPtr<Glib::Object> const item) -> Gtk::Widget *
{
auto participant = std::dynamic_pointer_cast<domain::participant>(item);
diff --git a/app/src/windows/tracker.cpp b/app/src/windows/tracker.cpp
index 5af405b..7712539 100644
--- a/app/src/windows/tracker.cpp
+++ b/app/src/windows/tracker.cpp
@@ -30,34 +30,23 @@ namespace turns::app::windows
, m_controls{builder->get_widget<Gtk::Revealer>("controls")}
, m_empty(builder->get_widget<Gtk::Widget>("empty"))
, 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()))
, m_turn_order{Gtk::make_managed<widgets::turn_order_view>()}
, m_subtitle{Glib::wrap(GTK_WIDGET(m_title)), "subtitle"}
{
- m_stack->add(*m_turn_order);
-
- auto clear_action = add_action("clear", sigc::mem_fun(*m_turn_order->get_model(), &domain::turn_order::clear));
- add_action("add_participant", sigc::mem_fun(*this, &tracker::handle_add_participant));
- 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));
- auto start_action = add_action("start", sigc::mem_fun(*m_turn_order->get_model(), &domain::turn_order::start));
-
- Glib::Binding::bind_property(m_turn_order->get_model()->property_empty(),
- clear_action->property_enabled(),
- Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN);
+ setup_actions();
- Glib::Binding::bind_property(m_turn_order->get_model()->property_empty(),
- m_controls->property_reveal_child(),
- Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN);
+ m_stack->add(*m_turn_order);
Glib::Binding::bind_property(m_turn_order->get_model()->property_empty(),
m_stack->property_visible_child(),
Glib::Binding::Flags::SYNC_CREATE,
[this](auto empty) { return empty ? m_empty : m_turn_order; });
- Glib::Binding::bind_property(m_turn_order->get_model()->property_empty(),
- start_action->property_enabled(),
- Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN);
+ Glib::Binding::bind_property(m_turn_order->get_model()->property_running(),
+ m_controls->property_reveal_child(),
+ Glib::Binding::Flags::SYNC_CREATE);
// clang-format off
Glib::Binding::bind_property(m_turn_order->get_model()->property_empty(),
@@ -91,4 +80,83 @@ namespace turns::app::windows
dialog->present(this);
}
+ auto tracker::handle_stop() -> void
+ {
+ m_turn_order->get_model()->stop();
+ }
+
+ auto tracker::setup_actions() -> void
+ {
+ // win.add_participant
+ // depends-on: turn_order:state == stopped
+ {
+ auto action = add_action("add_participant", sigc::mem_fun(*this, &tracker::handle_add_participant));
+
+ Glib::Binding::bind_property(m_turn_order->get_model()->property_running(),
+ action->property_enabled(),
+ Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN);
+ }
+
+ // win.clear
+ // depends-on: turn_order:empty == false
+ {
+ auto action = add_action("clear", sigc::mem_fun(*m_turn_order->get_model(), &domain::turn_order::clear));
+
+ Glib::Binding::bind_property(m_turn_order->get_model()->property_empty(),
+ action->property_enabled(),
+ Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN);
+ }
+
+ // win.next
+ // depends-on: turn_order:state == running
+ {
+ auto action = add_action("next", sigc::mem_fun(*m_turn_order->get_model(), &domain::turn_order::next));
+
+ Glib::Binding::bind_property(m_turn_order->get_model()->property_running(),
+ action->property_enabled(),
+ Glib::Binding::Flags::SYNC_CREATE);
+ }
+
+ // win.previous
+ // depends-on: turn_order:has_previous == true
+ {
+ auto action = add_action("previous", sigc::mem_fun(*m_turn_order->get_model(), &domain::turn_order::previous));
+
+ Glib::Binding::bind_property(m_turn_order->get_model()->property_has_previous(),
+ action->property_enabled(),
+ Glib::Binding::Flags::SYNC_CREATE);
+ }
+
+ // win.start
+ // depends-on: turn_order:empty == false
+ {
+ auto action = add_action("start", sigc::mem_fun(*m_turn_order->get_model(), &domain::turn_order::start));
+
+ Glib::Binding::bind_property(m_turn_order->get_model()->property_empty(),
+ action->property_enabled(),
+ Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN);
+
+ Glib::Binding::bind_property(m_turn_order->get_model()->property_running(),
+ m_start->property_visible(),
+ Glib::Binding::Flags::SYNC_CREATE | Glib::Binding::Flags::INVERT_BOOLEAN);
+ }
+
+ // win.stop
+ // depends-on: turn_order:running == true
+ {
+ auto action = add_action("stop", sigc::mem_fun(*this, &tracker::handle_stop));
+
+ Glib::Binding::bind_property(m_turn_order->get_model()->property_running(),
+ action->property_enabled(),
+ Glib::Binding::Flags::SYNC_CREATE);
+ }
+
+ // win.delete
+ // win.edit
+ {
+ 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));
+ }
+ }
+
} // namespace turns::app::windows
diff --git a/domain/include/turns/domain/turn_order.hpp b/domain/include/turns/domain/turn_order.hpp
index d3f74eb..59438d6 100644
--- a/domain/include/turns/domain/turn_order.hpp
+++ b/domain/include/turns/domain/turn_order.hpp
@@ -14,31 +14,56 @@ namespace turns::domain
struct turn_order : Glib::Object
{
+ using active_participant_type = unsigned int;
+ using empty_type = bool;
+ using has_next_type = bool;
+ using has_previous_type = bool;
+ using running_type = bool;
+ using round_type = unsigned int;
+
auto static create() -> Glib::RefPtr<turn_order>;
turn_order();
- auto add(Glib::ustring const & name, float priority, disposition disposition) -> void;
+ /** Modifiers */
+ auto add(Glib::ustring const & name, float priority, disposition disposition) -> void;
auto clear() -> void;
-
- auto empty() const noexcept -> bool;
-
- auto get(unsigned int index) -> Glib::RefPtr<participant>;
-
+ auto next() -> void;
+ auto previous() -> void;
auto remove(unsigned int index) -> void;
-
- auto size() -> unsigned int;
-
+ auto reset() -> void;
auto start() -> void;
+ auto stop() -> void;
+
+ /** Querries */
+ auto active_participant() const noexcept -> active_participant_type;
+ auto empty() const noexcept -> empty_type;
+ auto get(unsigned int index) const noexcept -> Glib::RefPtr<participant>;
auto list_model() -> Glib::RefPtr<Gio::ListModel>;
+ auto round() const noexcept -> round_type;
+ auto running() const noexcept -> running_type;
+ auto size() const noexcept -> unsigned int;
- auto property_empty() const -> Glib::PropertyProxy_ReadOnly<bool>;
+ /** Properties */
+
+ auto property_active_participant() const -> Glib::PropertyProxy_ReadOnly<active_participant_type>;
+ auto property_empty() const -> Glib::PropertyProxy_ReadOnly<empty_type>;
+ auto property_has_next() const -> Glib::PropertyProxy_ReadOnly<has_next_type>;
+ auto property_has_previous() const -> Glib::PropertyProxy_ReadOnly<has_previous_type>;
+ auto property_running() const -> Glib::PropertyProxy_ReadOnly<running_type>;
+ auto property_round() const -> Glib::PropertyProxy_ReadOnly<round_type>;
private:
Glib::RefPtr<Gio::ListStore<participant>> m_model;
- Glib::Property<bool> m_empty;
+
+ Glib::Property<active_participant_type> m_active_participant;
+ Glib::Property<empty_type> m_empty;
+ Glib::Property<has_next_type> m_has_next;
+ Glib::Property<has_previous_type> m_has_previous;
+ Glib::Property<running_type> m_running;
+ Glib::Property<round_type> m_round;
};
} // namespace turns::domain
diff --git a/domain/src/turn_order.cpp b/domain/src/turn_order.cpp
index 990e9c1..595b55d 100644
--- a/domain/src/turn_order.cpp
+++ b/domain/src/turn_order.cpp
@@ -3,6 +3,7 @@
#include "turns/domain/participant.hpp"
#include <compare>
+#include <limits>
#include <typeinfo>
#include <glibmm/binding.h>
@@ -39,50 +40,121 @@ namespace turns::domain
turn_order::turn_order()
: Glib::ObjectBase{typeid(turn_order)}
, m_model{Gio::ListStore<participant>::create()}
+ , m_active_participant(*this, "active_participant", std::numeric_limits<active_participant_type>::max())
, m_empty{*this, "empty", true}
+ , m_has_next{*this, "has-next", false}
+ , m_has_previous{*this, "has-previous", false}
+ , m_running{*this, "running", false}
+ , m_round{*this, "round", 0}
{
Glib::Binding::bind_property(m_model->property_n_items(), m_empty.get_proxy(), Glib::Binding::Flags::DEFAULT, [](auto n) {
return n == 0;
});
}
+ /** Modifiers */
+
auto turn_order::add(Glib::ustring const & name, float priority, disposition disposition) -> void
{
auto participant = participant::create(name, priority, disposition);
if (auto [found, index] = m_model->find(participant, equal_comparator); !found)
{
- m_model->insert_sorted(participant, comparator);
+ auto position = m_model->insert_sorted(participant, comparator);
participant->property_priority().signal_changed().connect([this] { m_model->sort(comparator); });
+
+ if (m_active_participant != std::numeric_limits<active_participant_type>::max() && position <= m_active_participant)
+ {
+ m_active_participant = m_active_participant + 1;
+ }
}
}
- auto turn_order::get(unsigned int index) -> Glib::RefPtr<participant>
+ auto turn_order::clear() -> void
{
- return m_model->get_item(index);
+ m_model->remove_all();
+ m_active_participant = std::numeric_limits<active_participant_type>::max();
+ m_has_next = false;
+ m_has_previous = false;
+ m_running = false;
}
- auto turn_order::list_model() -> Glib::RefPtr<Gio::ListModel>
+ auto turn_order::next() -> void
{
- return m_model;
+ m_active_participant = (m_active_participant + 1) % size();
+ if (!m_active_participant)
+ {
+ m_round = round() + 1;
+ }
+ m_has_previous = m_active_participant || m_round;
}
- auto turn_order::size() -> unsigned int
+ auto turn_order::previous() -> void
{
- return m_model->get_n_items();
+ if (!m_has_previous)
+ {
+ return;
+ }
+
+ if (m_active_participant)
+ {
+ m_active_participant = m_active_participant - 1;
+ }
+ else if (m_round)
+ {
+ m_round = round() - 1;
+ m_active_participant = size() - 1;
+ }
+
+ m_has_previous = m_active_participant || m_round;
}
auto turn_order::remove(unsigned int index) -> void
{
m_model->remove(index);
+ if (empty())
+ {
+ m_active_participant = std::numeric_limits<active_participant_type>::max();
+ m_has_next = false;
+ m_has_previous = false;
+ m_running = false;
+ }
+ else if (m_active_participant >= size() - 1)
+ {
+ m_active_participant = size() - 1;
+ }
+ else if (index <= m_active_participant)
+ {
+ m_active_participant = m_active_participant - 1;
+ }
}
- auto turn_order::clear() -> void
+ auto turn_order::reset() -> void
{
- m_model->remove_all();
+ m_running = false;
+ m_active_participant = 0;
}
auto turn_order::start() -> void
{
+ if (m_active_participant == std::numeric_limits<active_participant_type>::max())
+ {
+ m_active_participant = 0;
+ }
+ m_running = true;
+ m_has_next = true;
+ }
+
+ auto turn_order::stop() -> void
+ {
+ m_running = false;
+ m_has_next = false;
+ }
+
+ /** Querries */
+
+ auto turn_order::active_participant() const noexcept -> active_participant_type
+ {
+ return m_active_participant;
}
auto turn_order::empty() const noexcept -> bool
@@ -90,9 +162,61 @@ namespace turns::domain
return m_empty;
}
+ auto turn_order::get(unsigned int index) const noexcept -> Glib::RefPtr<participant>
+ {
+ return m_model->get_item(index);
+ }
+
+ auto turn_order::list_model() -> Glib::RefPtr<Gio::ListModel>
+ {
+ return m_model;
+ }
+
+ auto turn_order::round() const noexcept -> round_type
+ {
+ return m_round;
+ }
+
+ auto turn_order::running() const noexcept -> running_type
+ {
+ return m_running;
+ }
+
+ auto turn_order::size() const noexcept -> unsigned int
+ {
+ return m_model->get_n_items();
+ }
+
+ /** Properties */
+
+ auto turn_order::property_active_participant() const -> Glib::PropertyProxy_ReadOnly<active_participant_type>
+ {
+ return m_active_participant.get_proxy();
+ }
+
auto turn_order::property_empty() const -> Glib::PropertyProxy_ReadOnly<bool>
{
return m_empty.get_proxy();
}
+ auto turn_order::property_has_next() const -> Glib::PropertyProxy_ReadOnly<has_next_type>
+ {
+ return m_has_next.get_proxy();
+ }
+
+ auto turn_order::property_has_previous() const -> Glib::PropertyProxy_ReadOnly<has_previous_type>
+ {
+ return m_has_previous.get_proxy();
+ }
+
+ auto turn_order::property_running() const -> Glib::PropertyProxy_ReadOnly<running_type>
+ {
+ return m_running.get_proxy();
+ }
+
+ auto turn_order::property_round() const -> Glib::PropertyProxy_ReadOnly<round_type>
+ {
+ return m_round.get_proxy();
+ }
+
} // namespace turns::domain \ No newline at end of file
diff --git a/res/ui.cmb b/res/ui.cmb
index 1bfae73..1a29628 100644
--- a/res/ui.cmb
+++ b/res/ui.cmb
@@ -92,7 +92,7 @@
(1,22,"GtkActionable","action-name","win.previous",None,None,None,None,None,None,None,None,None),
(1,22,"GtkButton","icon-name","media-skip-backward-symbolic",None,None,None,None,None,None,None,None,None),
(1,22,"GtkWidget","tooltip-markup","Previous participant",None,None,None,None,None,None,None,None,None),
- (1,23,"GtkActionable","action-name","win.end",None,None,None,None,None,None,None,None,None),
+ (1,23,"GtkActionable","action-name","win.stop",None,None,None,None,None,None,None,None,None),
(1,23,"GtkButton","icon-name","media-playback-stop-symbolic",None,None,None,None,None,None,None,None,None),
(1,23,"GtkWidget","tooltip-markup","End turn order",None,None,None,None,None,None,None,None,None),
(1,24,"GtkActionable","action-name","win.next",None,None,None,None,None,None,None,None,None),
@@ -122,8 +122,8 @@
(2,12,"GtkAdjustment","lower","-1000.0",None,None,None,None,None,None,None,None,None),
(2,12,"GtkAdjustment","step-increment","1.0",None,None,None,None,None,None,None,None,None),
(2,12,"GtkAdjustment","upper","1000.0",None,None,None,None,None,None,None,None,None),
- (3,1,"GtkListBoxRow","activatable","False",None,None,None,None,None,None,None,None,None),
(3,1,"GtkListBoxRow","child",None,None,None,None,None,2,None,None,None,None),
+ (3,1,"GtkListBoxRow","selectable","False",None,None,None,None,None,None,None,None,None),
(3,1,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(3,4,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(3,4,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),