[gnome-tetravex] Improve end-of-game UX.
- From: Arnaud B. <arnaudb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-tetravex] Improve end-of-game UX.
- Date: Fri, 11 Oct 2019 01:36:43 +0000 (UTC)
commit ed243e1a96372e5fb8d436fcb270035277c9dcac
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date: Sun Sep 22 20:21:18 2019 +0200
Improve end-of-game UX.
Final work on issue #3.
Fixes #3, and fixes #6.
po/POTFILES.in | 2 +
po/POTFILES.skip | 1 +
src/gnome-tetravex.gresource.xml | 5 +-
src/gnome-tetravex.vala | 85 ++++++++++++-------
src/history.vala | 175 +++++++++++++++++++++++++++++++++------
src/meson.build | 1 +
src/puzzle-view.vala | 61 ++++++++++++--
src/score-dialog.vala | 44 ++--------
src/score-overlay-entry.ui | 34 ++++++++
src/score-overlay.ui | 72 ++++++++++++++++
src/score-overlay.vala | 164 ++++++++++++++++++++++++++++++++++++
src/tetravex.css | 43 ++++++++++
src/theme-extrusion.vala | 16 +++-
src/theme-neoretro.vala | 51 ++++++++----
src/theme-nostalgia.vala | 61 +++++++++-----
src/theme-synesthesia.vala | 57 +++++++++----
16 files changed, 720 insertions(+), 152 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6ba9966..e1204e8 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -10,6 +10,8 @@ src/help-overlay.ui
src/puzzle.vala
src/puzzle-view.vala
src/score-dialog.vala
+src/score-overlay.vala
+src/score-overlay.ui
src/theme-extrusion.vala
src/theme-neoretro.vala
src/theme-nostalgia.vala
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 27438b3..1bc8973 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -2,6 +2,7 @@ src/gnome-tetravex.c
src/puzzle.c
src/puzzle-view.c
src/score-dialog.c
+src/score-overlay.c
src/theme-extrusion.c
src/theme-neoretro.c
src/theme-nostalgia.c
diff --git a/src/gnome-tetravex.gresource.xml b/src/gnome-tetravex.gresource.xml
index e405d53..4ee78ee 100644
--- a/src/gnome-tetravex.gresource.xml
+++ b/src/gnome-tetravex.gresource.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/Tetravex">
- <file preprocess="xml-stripblanks">gnome-tetravex.ui</file>
<file preprocess="xml-stripblanks">app-menu.ui</file>
+ <file preprocess="xml-stripblanks">gnome-tetravex.ui</file>
+ <file preprocess="xml-stripblanks">score-overlay.ui</file>
+ <file preprocess="xml-stripblanks">score-overlay-entry.ui</file>
+ <file>tetravex.css</file>
</gresource>
<gresource prefix="/org/gnome/Tetravex/gtk">
<file preprocess="xml-stripblanks">help-overlay.ui</file>
diff --git a/src/gnome-tetravex.vala b/src/gnome-tetravex.vala
index 1252879..7d958f4 100644
--- a/src/gnome-tetravex.vala
+++ b/src/gnome-tetravex.vala
@@ -45,6 +45,8 @@ private class Tetravex : Gtk.Application
private SimpleAction solve_action;
private SimpleAction finish_action;
+ private ScoreOverlay score_overlay;
+
private const OptionEntry [] option_entries =
{
/* Translators: command-line option description, see 'gnome-tetravex --help' */
@@ -125,6 +127,12 @@ private class Tetravex : Gtk.Application
history = new History (Path.build_filename (Environment.get_user_data_dir (), "gnome-tetravex",
"history"));
+ CssProvider css_provider = new CssProvider ();
+ css_provider.load_from_resource ("/org/gnome/Tetravex/tetravex.css");
+ Gdk.Screen? gdk_screen = Gdk.Screen.get_default ();
+ if (gdk_screen != null) // else..?
+ StyleContext.add_provider_for_screen ((!) gdk_screen, css_provider,
STYLE_PROVIDER_PRIORITY_APPLICATION);
+
window = (ApplicationWindow) builder.get_object ("gnome-tetravex-window");
this.add_window (window);
window.key_press_event.connect (on_key_press_event);
@@ -175,7 +183,22 @@ private class Tetravex : Gtk.Application
view.vexpand = true;
view.button_release_event.connect (view_button_release_event);
settings.bind ("theme", view, "theme-id", SettingsBindFlags.GET | SettingsBindFlags.NO_SENSITIVITY);
- grid.attach (view, 0, 0, 3, 1);
+
+ Overlay overlay = new Overlay ();
+ overlay.add (view);
+ overlay.show ();
+
+ score_overlay = new ScoreOverlay ();
+ overlay.add_overlay (score_overlay);
+ overlay.set_overlay_pass_through (score_overlay, true);
+
+ view.bind_property ("boardsize", score_overlay, "boardsize", BindingFlags.DEFAULT |
BindingFlags.SYNC_CREATE);
+ view.bind_property ("x-offset-right", score_overlay, "margin-left", BindingFlags.DEFAULT |
BindingFlags.SYNC_CREATE);
+ view.bind_property ("right-margin", score_overlay, "margin-right", BindingFlags.DEFAULT |
BindingFlags.SYNC_CREATE);
+ view.bind_property ("y-offset", score_overlay, "margin-top", BindingFlags.DEFAULT |
BindingFlags.SYNC_CREATE);
+ view.bind_property ("y-offset", score_overlay, "margin-bottom", BindingFlags.DEFAULT |
BindingFlags.SYNC_CREATE);
+
+ grid.attach (overlay, 0, 0, 3, 1);
settings.bind ("mouse-use-extra-buttons", view,
"mouse-use-extra-buttons", SettingsBindFlags.GET |
SettingsBindFlags.NO_SENSITIVITY);
@@ -356,11 +379,13 @@ private class Tetravex : Gtk.Application
private void new_game ()
{
+ puzzle_is_finished = false;
has_been_finished = false;
pause_action.set_enabled (true);
solve_action.set_enabled (true);
finish_action.set_enabled (false);
new_game_solve_stack.set_visible_child_name ("solve");
+ score_overlay.hide ();
if (puzzle_init_done)
SignalHandler.disconnect_by_func (puzzle, null, this);
@@ -401,8 +426,10 @@ private class Tetravex : Gtk.Application
clock_label.set_text ("%02d∶\xE2\x80\x8E%02d".printf (minutes, seconds));
}
+ private bool puzzle_is_finished = false;
private void solved_cb (Puzzle puzzle)
{
+ puzzle_is_finished = true;
undo_action.set_enabled (false);
redo_action.set_enabled (false);
pause_action.set_enabled (false);
@@ -431,35 +458,25 @@ private class Tetravex : Gtk.Application
{
DateTime date = new DateTime.now_local ();
uint duration = (uint) (puzzle.elapsed + 0.5);
- HistoryEntry entry = new HistoryEntry (date, puzzle.size, duration);
- history.add (entry);
- history.save ();
-
- int score_dialog_action = show_scores (entry, true);
- if (score_dialog_action == ResponseType.CLOSE)
- window.destroy ();
- else if (score_dialog_action == ResponseType.OK)
- new_game ();
- else if (score_dialog_action != ResponseType.REJECT)
- new_game_solve_stack.set_visible_child_name ("new-game");
- }
+ last_history_entry = new HistoryEntry (date, puzzle.size, duration);
- private bool scores_dialog_visible = false; // security for #5
- private int show_scores (HistoryEntry? selected_entry = null, bool show_quit = false)
- {
- if (scores_dialog_visible)
- return ResponseType.REJECT;
+ if (!puzzle_is_finished) // Ctrl-n has been hit before the animation finished
+ return;
- scores_dialog_visible = true;
- ScoreDialog dialog = new ScoreDialog (history, puzzle.size, selected_entry, show_quit);
- dialog.set_modal (true);
- dialog.set_transient_for (window);
+ HistoryEntry? other_score_0;
+ HistoryEntry? other_score_1;
+ HistoryEntry? other_score_2;
+ uint position = history.get_place ((!) last_history_entry,
+ puzzle.size,
+ out other_score_0,
+ out other_score_1,
+ out other_score_2);
+ score_overlay.set_score (puzzle.size, position, (!) last_history_entry, other_score_0,
other_score_1, other_score_2);
- int result = dialog.run ();
- dialog.destroy ();
- scores_dialog_visible = false;
+ new_game_solve_stack.set_visible_child_name ("new-game");
+ view.hide_right_sockets ();
- return result;
+ score_overlay.show ();
}
private void new_game_cb ()
@@ -489,9 +506,21 @@ private class Tetravex : Gtk.Application
new_game ();
}
- private void scores_cb ()
+ private HistoryEntry? last_history_entry = null;
+ private bool scores_dialog_visible = false; // security for #5
+ private void scores_cb (/* SimpleAction action, Variant? variant */)
{
- show_scores ();
+ if (scores_dialog_visible)
+ return;
+
+ scores_dialog_visible = true;
+ ScoreDialog dialog = new ScoreDialog (history, puzzle.size, puzzle.is_solved ? last_history_entry :
null);
+ dialog.set_modal (true);
+ dialog.set_transient_for (window);
+
+ dialog.run ();
+ dialog.destroy ();
+ scores_dialog_visible = false;
}
private bool view_button_release_event (Widget widget, Gdk.EventButton event)
diff --git a/src/history.vala b/src/history.vala
index 822df20..aab7d41 100644
--- a/src/history.vala
+++ b/src/history.vala
@@ -14,21 +14,108 @@ private class History : Object
[CCode (notify = false)] public string filename { private get; protected construct; }
internal List<HistoryEntry> entries = new List<HistoryEntry> ();
+ /*\
+ * * getting
+ \*/
+
internal signal void entry_added (HistoryEntry entry);
- internal History (string filename)
+ internal uint get_place (HistoryEntry entry,
+ uint8 puzzle_size,
+ out HistoryEntry? other_entry_0,
+ out HistoryEntry? other_entry_1,
+ out HistoryEntry? other_entry_2)
{
- Object (filename: filename);
- load ();
+ entries.insert_sorted (entry, HistoryEntry.compare_entries);
+ entry_added (entry);
+ save ();
+
+ unowned List<HistoryEntry> entry_item = entries.find (entry);
+ unowned List<HistoryEntry> best_time_item;
+ uint best_position = get_best_time_position (entry_item, out best_time_item);
+ uint position = entries.position (entry_item) - best_position + 1;
+ switch (position)
+ {
+ case 1:
+ unowned List<HistoryEntry>? tmp_item = entry_item.next;
+ if (tmp_item == null || ((!) tmp_item).data.size != puzzle_size)
+ {
+ other_entry_0 = null;
+ other_entry_1 = null;
+ other_entry_2 = null;
+ break;
+ }
+ other_entry_0 = ((!) tmp_item).data;
+ tmp_item = ((!) tmp_item).next;
+ if (tmp_item == null || ((!) tmp_item).data.size != puzzle_size)
+ {
+ other_entry_1 = null;
+ other_entry_2 = null;
+ break;
+ }
+ other_entry_1 = ((!) tmp_item).data;
+ tmp_item = ((!) tmp_item).next;
+ if (tmp_item == null || ((!) tmp_item).data.size != puzzle_size)
+ other_entry_2 = null;
+ else
+ other_entry_2 = ((!) tmp_item).data;
+ break;
+
+ case 2:
+ other_entry_0 = best_time_item.data;
+ unowned List<HistoryEntry>? tmp_item = entry_item.next;
+ if (tmp_item == null || ((!) tmp_item).data.size != puzzle_size)
+ {
+ other_entry_1 = null;
+ other_entry_2 = null;
+ break;
+ }
+ other_entry_1 = ((!) tmp_item).data;
+ tmp_item = ((!) tmp_item).next;
+ if (tmp_item == null || ((!) tmp_item).data.size != puzzle_size)
+ other_entry_2 = null;
+ else
+ other_entry_2 = ((!) tmp_item).data;
+ break;
+
+ default:
+ other_entry_0 = best_time_item.data;
+ other_entry_1 = entry_item.prev.data;
+ unowned List<HistoryEntry>? next_entry_item = entry_item.next;
+ if (next_entry_item == null || ((!) next_entry_item).data.size != puzzle_size)
+ other_entry_2 = null;
+ else
+ other_entry_2 = ((!) next_entry_item).data;
+ break;
+ }
+ return position;
}
- internal void add (HistoryEntry entry)
+ private uint get_best_time_position (List<HistoryEntry> entry_item, out unowned List<HistoryEntry>
best_time_item)
{
- entries.append (entry);
- entry_added (entry);
+ uint8 puzzle_size = entry_item.data.size;
+ best_time_item = entries.first ();
+ if (puzzle_size == 2 || entry_item == best_time_item)
+ return 0;
+
+ best_time_item = entry_item;
+ do { best_time_item = best_time_item.prev; }
+ while (best_time_item != entries && best_time_item.data.size == puzzle_size);
+ best_time_item = best_time_item.next;
+ return entries.position (best_time_item);
+ }
+
+ /*\
+ * * loading
+ \*/
+
+ internal History (string filename)
+ {
+ Object (filename: filename);
+ load ();
}
- internal void load ()
+ private inline void load ()
{
string contents = "";
try
@@ -57,11 +144,34 @@ private class History : Object
// FIXME use try_parse
- add (new HistoryEntry ((!) date, size, duration));
+ entries.prepend (new HistoryEntry ((!) date, size, duration));
}
+ entries.sort (HistoryEntry.compare_entries);
+ }
+
+ private inline DateTime? parse_date (string date)
+ {
+ if (date.length < 19 || date[4] != '-' || date[7] != '-' || date[10] != 'T' || date[13] != ':' ||
date[16] != ':')
+ return null;
+
+ // FIXME use try_parse
+
+ int year = int.parse (date.substring (0, 4));
+ int month = int.parse (date.substring (5, 2));
+ int day = int.parse (date.substring (8, 2));
+ int hour = int.parse (date.substring (11, 2));
+ int minute = int.parse (date.substring (14, 2));
+ int seconds = int.parse (date.substring (17, 2));
+ string timezone = date.substring (19);
+
+ return new DateTime (new TimeZone (timezone), year, month, day, hour, minute, seconds);
}
- internal void save ()
+ /*\
+ * * saving
+ \*/
+
+ private inline void save ()
{
string contents = "";
@@ -81,24 +191,6 @@ private class History : Object
warning ("Failed to save history: %s", e.message);
}
}
-
- private DateTime? parse_date (string date)
- {
- if (date.length < 19 || date[4] != '-' || date[7] != '-' || date[10] != 'T' || date[13] != ':' ||
date[16] != ':')
- return null;
-
- // FIXME use try_parse
-
- int year = int.parse (date.substring (0, 4));
- int month = int.parse (date.substring (5, 2));
- int day = int.parse (date.substring (8, 2));
- int hour = int.parse (date.substring (11, 2));
- int minute = int.parse (date.substring (14, 2));
- int seconds = int.parse (date.substring (17, 2));
- string timezone = date.substring (19);
-
- return new DateTime (new TimeZone (timezone), year, month, day, hour, minute, seconds);
- }
}
private class HistoryEntry : Object // TODO make struct? needs using HistoryEntry? for the List...
@@ -111,4 +203,33 @@ private class HistoryEntry : Object // TODO make struct? needs using HistoryEntr
{
Object (date: date, size: size, duration: duration);
}
+
+ /*\
+ * * utilities
+ \*/
+
+ internal static string get_duration_string (uint duration)
+ {
+ if (duration >= 3600)
+ /* Translators: that is the duration of a game, as seen in the Scores dialog, if game has taken
one hour or more; the %u are replaced by the hours (h), minutes (m) and seconds (s); as an example, you might
want to use "%u:%.2u:%.2u", that is quite international (the ".2" meaning "two digits, padding with 0") */
+ return _("%uh %um %us").printf (duration / 3600, (duration / 60) % 60, duration % 60);
+
+ if (duration >= 60)
+ /* Translators: that is the duration of a game, as seen in the Scores dialog, if game has taken
between one minute and one hour; the %u are replaced by the minutes (m) and seconds (s); as an example, you
might want to use "%.2u:%.2u", that is quite international (the ".2" meaning "two digits, padding with 0") */
+ return _("%um %us").printf (duration / 60, duration % 60);
+
+ else
+ /* Translators: that is the duration of a game, as seen in the Scores dialog, if game has taken
less than one minute; the %u is replaced by the number of seconds (s) it has taken; as an example, you might
want to use "00:%.2u", that is quite international (the ".2" meaning "two digits, padding with 0") */
+ return _("%us").printf (duration);
+ }
+
+ internal static int compare_entries (HistoryEntry a, HistoryEntry b)
+ {
+ if (a.size != b.size)
+ return (int) a.size - (int) b.size;
+ if (a.duration != b.duration)
+ return (int) a.duration - (int) b.duration;
+ else
+ return a.date.compare (b.date);
+ }
}
diff --git a/src/meson.build b/src/meson.build
index 5269f8c..55c2674 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -9,6 +9,7 @@ sources = files (
'puzzle.vala',
'puzzle-view.vala',
'score-dialog.vala',
+ 'score-overlay.vala',
'theme-extrusion.vala',
'theme-neoretro.vala',
'theme-nostalgia.vala',
diff --git a/src/puzzle-view.vala b/src/puzzle-view.vala
index 0fe08bc..1e08365 100644
--- a/src/puzzle-view.vala
+++ b/src/puzzle-view.vala
@@ -22,6 +22,7 @@ private abstract class Theme : Object
internal abstract void draw_socket (Cairo.Context context);
internal abstract void draw_paused_tile (Cairo.Context context);
internal abstract void draw_tile (Cairo.Context context, Tile tile);
+ internal abstract void set_animation_level (uint8 animation_level /* 0-16 */);
}
private class PuzzleView : Gtk.DrawingArea
@@ -74,6 +75,7 @@ private class PuzzleView : Gtk.DrawingArea
private get { if (!puzzle_init_done) assert_not_reached (); return _puzzle; }
internal set
{
+ show_right_sockets ();
uint8 old_puzzle_size = 0;
if (puzzle_init_done)
{
@@ -131,6 +133,7 @@ private class PuzzleView : Gtk.DrawingArea
theme.configure (tilesize);
arrow_pattern = null;
socket_pattern = null;
+ theme.set_animation_level (socket_animation_level);
queue_draw ();
}
}
@@ -155,8 +158,11 @@ private class PuzzleView : Gtk.DrawingArea
private uint animation_timeout = 0;
/* Set in configure event */
+ [CCode (notify = true)] internal uint boardsize { internal get; private set; default = 0; }
+ [CCode (notify = true)] internal double x_offset_right { internal get; private set; default = 0; }
+ [CCode (notify = true)] internal double y_offset { internal get; private set; default = 0; }
+ [CCode (notify = true)] internal double right_margin { internal get; private set; default = 0; }
private double x_offset = 0.0;
- private double y_offset = 0.0;
private uint tilesize = 0;
private uint gap = 0;
private double arrow_x = 0.0;
@@ -165,7 +171,6 @@ private class PuzzleView : Gtk.DrawingArea
private double [,] sockets_ys;
private int board_x_maxi = 0;
private int board_y_maxi = 0;
- private int boardsize = 0;
private double snap_distance = 0.0;
/* Pre-rendered image */
@@ -330,6 +335,9 @@ private class PuzzleView : Gtk.DrawingArea
arrow_x = x_offset + boardsize;
arrow_local_y = boardsize * 0.5;
+ x_offset_right = arrow_x + gap;
+ right_margin = allocated_width - x_offset_right - boardsize;
+
/* Precalculate sockets positions */
for (uint y = 0; y < puzzle.size; y++)
for (uint x = 0; x < puzzle.size * 2; x++)
@@ -371,7 +379,7 @@ private class PuzzleView : Gtk.DrawingArea
/* arrow pattern */
tmp_surface = new Cairo.Surface.similar (context.get_target (), Cairo.Content.COLOR_ALPHA, (int) gap,
-
boardsize);
+ (int)
boardsize);
tmp_context = new Cairo.Context (tmp_surface);
tmp_context.save ();
@@ -536,7 +544,7 @@ private class PuzzleView : Gtk.DrawingArea
private bool on_right_half (double x)
{
- return x > x_offset + boardsize + gap * 0.5;
+ return x > x_offset_right - gap * 0.5;
}
private void drop_tile (double x, double y)
@@ -555,7 +563,7 @@ private class PuzzleView : Gtk.DrawingArea
int16 tile_x;
if (on_right_half (x))
{
- tile_x = (int16) puzzle.size + (int16) Math.floor ((x - (x_offset + boardsize + gap)) /
tilesize);
+ tile_x = (int16) puzzle.size + (int16) Math.floor ((x - x_offset_right) / tilesize);
tile_x = tile_x.clamp ((int16) puzzle.size, 2 * (int16) puzzle.size - 1);
}
else
@@ -764,6 +772,10 @@ private class PuzzleView : Gtk.DrawingArea
tile_selected = false;
}
+ /*\
+ * * history proxies
+ \*/
+
internal void undo ()
{
last_selected_tile = null;
@@ -775,4 +787,43 @@ private class PuzzleView : Gtk.DrawingArea
last_selected_tile = null;
puzzle.redo ();
}
+
+ /*\
+ * * final animation
+ \*/
+
+ private uint8 socket_animation_level = 0;
+ private uint socket_timeout_id = 0;
+
+ internal void hide_right_sockets ()
+ {
+ socket_timeout_id = Timeout.add (75, () => {
+ socket_animation_level++;
+ theme.set_animation_level (socket_animation_level);
+ arrow_pattern = null;
+ socket_pattern = null;
+ queue_draw ();
+
+ if (socket_animation_level < 17)
+ return Source.CONTINUE;
+ else
+ {
+ socket_timeout_id = 0;
+ return Source.REMOVE;
+ }
+ });
+ }
+
+ private inline void show_right_sockets ()
+ {
+ if (socket_timeout_id != 0)
+ {
+ Source.remove (socket_timeout_id);
+ socket_timeout_id = 0;
+ }
+ socket_animation_level = 0;
+ theme.set_animation_level (0);
+ arrow_pattern = null;
+ socket_pattern = null;
+ }
}
diff --git a/src/score-dialog.vala b/src/score-dialog.vala
index 45350a4..49560ae 100644
--- a/src/score-dialog.vala
+++ b/src/score-dialog.vala
@@ -20,26 +20,14 @@ private class ScoreDialog : Dialog
private ComboBox size_combo;
private TreeView scores;
- internal ScoreDialog (History history, uint8 size, HistoryEntry? selected_entry = null, bool show_quit =
false)
+ internal ScoreDialog (History history, uint8 size, HistoryEntry? selected_entry = null)
{
+ Object (use_header_bar: /* true */ 1);
+
this.history = history;
history.entry_added.connect (entry_added_cb);
this.selected_entry = selected_entry;
- if (show_quit)
- {
- /* Translators: label of a button of the Scores dialog, as it is displayed at the end of a game;
quits the application */
- add_button (_("Quit"), ResponseType.CLOSE);
-
-
- /* Translators: label of a button of the Scores dialog, as it is displayed at the end of a game;
starts a new game */
- add_button (_("New Game"), ResponseType.OK);
- }
- else
- {
- /* Translators: label of a button of the Scores dialog, as it is displayed when called from the
hamburger menu; closes the dialog */
- add_button (_("OK"), ResponseType.DELETE_EVENT);
- }
set_size_request (200, 300);
Box vbox = new Box (Orientation.VERTICAL, 5);
@@ -88,7 +76,7 @@ private class ScoreDialog : Dialog
scroll.add (scores);
List<unowned HistoryEntry> entries = history.entries.copy ();
- entries.sort (compare_entries);
+ entries.sort (HistoryEntry.compare_entries);
foreach (HistoryEntry entry in entries)
entry_added_cb (entry);
@@ -102,7 +90,7 @@ private class ScoreDialog : Dialog
score_model.clear ();
List<unowned HistoryEntry> entries = history.entries.copy ();
- entries.sort (compare_entries);
+ entries.sort (HistoryEntry.compare_entries);
foreach (HistoryEntry entry in entries)
{
@@ -112,18 +100,7 @@ private class ScoreDialog : Dialog
/* "the preferred date representation for the current locale without the time" */
string date_label = entry.date.format ("%x");
- string time_label;
- if (entry.duration >= 3600)
- /* Translators: that is the duration of a game, as seen in the Scores dialog, if game has
taken one hour or more; the %u are replaced by the hours (h), minutes (m) and seconds (s); as an example, you
might want to use "%u:%.2u:%.2u", that is quite international (the ".2" meaning "two digits, padding with 0")
*/
- time_label = _("%uh %um %us").printf (entry.duration / 3600, (entry.duration / 60) % 60,
entry.duration % 60);
-
- else if (entry.duration >= 60)
- /* Translators: that is the duration of a game, as seen in the Scores dialog, if game has
taken between one minute and one hour; the %u are replaced by the minutes (m) and seconds (s); as an example,
you might want to use "%.2u:%.2u", that is quite international (the ".2" meaning "two digits, padding with
0") */
- time_label = _("%um %us").printf (entry.duration / 60, entry.duration % 60);
-
- else
- /* Translators: that is the duration of a game, as seen in the Scores dialog, if game has
taken less than one minute; the %u is replaced by the number of seconds (s) it has taken; as an example, you
might want to use "00:%.2u", that is quite international (the ".2" meaning "two digits, padding with 0") */
- time_label = _("%us").printf (entry.duration);
+ string time_label = HistoryEntry.get_duration_string (entry.duration);
int weight = Pango.Weight.NORMAL;
if (entry == selected_entry)
@@ -149,15 +126,6 @@ private class ScoreDialog : Dialog
}
}
- private static int compare_entries (HistoryEntry a, HistoryEntry b)
- {
- if (a.size != b.size)
- return (int) a.size - (int) b.size;
- if (a.duration != b.duration)
- return (int) a.duration - (int) b.duration;
- return a.date.compare (b.date);
- }
-
private void size_changed_cb (ComboBox combo)
{
TreeIter iter;
diff --git a/src/score-overlay-entry.ui b/src/score-overlay-entry.ui
new file mode 100644
index 0000000..780e5ee
--- /dev/null
+++ b/src/score-overlay-entry.ui
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <template class="ScoreOverlayEntry" parent="GtkGrid">
+ <property name="visible">True</property>
+ <property name="width-request">160</property> <!-- two tiles of 80 max -->
+ <property name="halign">center</property>
+ <child>
+ <object class="GtkLabel" id="place_label">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="halign">start</property>
+ <style>
+ <class name="bold-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="top-attach">2</property>
+ <property name="left-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="value_label">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="halign">end</property>
+ </object>
+ <packing>
+ <property name="top-attach">2</property>
+ <property name="left-attach">2</property>
+ </packing>
+ </child>
+ </template>
+</interface>
diff --git a/src/score-overlay.ui b/src/score-overlay.ui
new file mode 100644
index 0000000..72bd54b
--- /dev/null
+++ b/src/score-overlay.ui
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <template class="ScoreOverlay" parent="GtkGrid">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="orientation">vertical</property>
+ <property name="row-homogeneous">True</property>
+ <property name="column-homogeneous">True</property>
+ <property name="column-spacing">4</property>
+ <style>
+ <class name="score-overlay"/>
+ </style>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Congratulations!</property>
+ <style>
+ <class name="score-title"/>
+ </style>
+ </object>
+ <packing>
+ <property name="top-attach">0</property>
+ <property name="left-attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="ScoreOverlayEntry" id="score_0"/>
+ <packing>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="ScoreOverlayEntry" id="score_1"/>
+ <packing>
+ <property name="top-attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="ScoreOverlayEntry" id="score_2"/>
+ <packing>
+ <property name="top-attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="ScoreOverlayEntry" id="score_3"/>
+ <packing>
+ <property name="top-attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes">Show scores</property>
+ <property name="action-name">app.scores</property>
+ <property name="focus-on-click">False</property>
+ <style>
+ <class name="rounded-button"/>
+ </style>
+ </object>
+ <packing>
+ <property name="top-attach">6</property>
+ <property name="left-attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </template>
+</interface>
diff --git a/src/score-overlay.vala b/src/score-overlay.vala
new file mode 100644
index 0000000..27e2ccc
--- /dev/null
+++ b/src/score-overlay.vala
@@ -0,0 +1,164 @@
+/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * Copyright (C) 2019 Arnaud Bonatti
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 2 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Tetravex/score-overlay.ui")]
+private class ScoreOverlay : Grid
+{
+ [CCode (notify = true)] internal uint boardsize
+ {
+ internal set
+ {
+ if (value < 250 && value != 0)
+ get_style_context ().add_class ("small-window");
+ else
+ get_style_context ().remove_class ("small-window");
+ }
+ }
+
+ /*\
+ * * updating labels
+ \*/
+
+ [GtkChild] private ScoreOverlayEntry score_0;
+ [GtkChild] private ScoreOverlayEntry score_1;
+ [GtkChild] private ScoreOverlayEntry score_2;
+ [GtkChild] private ScoreOverlayEntry score_3;
+
+ internal void set_score (uint8 puzzle_size,
+ uint /* [1[ */ position,
+ HistoryEntry entry,
+ HistoryEntry? other_entry_0,
+ HistoryEntry? other_entry_1,
+ HistoryEntry? other_entry_2)
+ {
+ switch (position)
+ {
+ case 1:
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has been the fastest for a puzzle of this size; introduces the game time */
+ score_0.set_place_label (_("New best time!"));
+ score_0.set_value_label (HistoryEntry.get_duration_string (entry.duration), true);
+
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has been the fastest for a puzzle of this size; introduces the old best time */
+ score_1.set_place_label (_("Second:"));
+ if (other_entry_0 != null)
+ score_1.set_value_label (HistoryEntry.get_duration_string (((!)
other_entry_0).duration));
+ else
+ score_1.set_value_label (null);
+
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has been the fastest for a puzzle of this size; introduces the old second best time */
+ score_2.set_place_label (_("Third:"));
+ if (other_entry_1 != null)
+ score_2.set_value_label (HistoryEntry.get_duration_string (((!)
other_entry_1).duration));
+ else
+ score_2.set_value_label (null);
+
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has been the fastest for a puzzle of this size; introduces the old third best time */
+ score_3.set_place_label (_("Out of podium:"));
+ if (other_entry_2 != null)
+ score_3.set_value_label (HistoryEntry.get_duration_string (((!)
other_entry_2).duration));
+ else
+ score_3.set_value_label (null);
+ break;
+
+ case 2:
+ if (other_entry_0 == null)
+ assert_not_reached ();
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has made the second best time for a puzzle of this size; introduces the best time ever */
+ score_0.set_place_label (_("Best time:"));
+ score_0.set_value_label (HistoryEntry.get_duration_string (((!) other_entry_0).duration));
+
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has made the second best time for a puzzle of this size; introduces the game time */
+ score_1.set_place_label (_("Your time:"));
+ score_1.set_value_label (HistoryEntry.get_duration_string (entry.duration), true);
+
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has made the second best time for a puzzle of this size; introduces the old second best time */
+ score_2.set_place_label (_("Third:"));
+ if (other_entry_1 != null)
+ score_2.set_value_label (HistoryEntry.get_duration_string (((!)
other_entry_1).duration));
+ else
+ score_2.set_value_label (null);
+
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has made the second best time for a puzzle of this size; introduces the old third best time */
+ score_3.set_place_label (_("Out of podium:"));
+ if (other_entry_2 != null)
+ score_3.set_value_label (HistoryEntry.get_duration_string (((!)
other_entry_2).duration));
+ else
+ score_3.set_value_label (null);
+ break;
+
+ default:
+ if (other_entry_0 == null || other_entry_1 == null)
+ assert_not_reached ();
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has not made the first or second best time for a puzzle of this size; introduces the best time
ever */
+ score_0.set_place_label (_("Best time:"));
+ score_0.set_value_label (HistoryEntry.get_duration_string (((!) other_entry_0).duration));
+
+ if (position == 3)
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has not made the first or second best time for a puzzle of this size; introduces the second best
time */
+ score_1.set_place_label (_("Second:"));
+
+ else if (position == 4)
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has not made the first or second best time for a puzzle of this size; introduces the third best
time */
+ score_1.set_place_label (_("Third:"));
+
+ else
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has not made the first or second best time for a puzzle of this size; the %u is replaced by the
rank before the one of the game played */
+ score_1.set_place_label (_("Place %u:").printf (position - 1));
+ score_1.set_value_label (HistoryEntry.get_duration_string (((!) other_entry_1).duration));
+
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has not made the first or second best time for a puzzle of this size; introduces the game time */
+ score_2.set_place_label (_("Your time:"));
+ score_2.set_value_label (HistoryEntry.get_duration_string (entry.duration), true);
+
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if
the player has not made the first or second best time for a puzzle of this size; the %u is replaced by the
rank after the one of the game played */
+ score_3.set_place_label (_("Place %u:").printf (position + 1));
+ if (other_entry_2 != null)
+ score_3.set_value_label (HistoryEntry.get_duration_string (((!)
other_entry_2).duration));
+ else
+ score_3.set_value_label (null);
+ break;
+ }
+ }
+}
+
+[GtkTemplate (ui = "/org/gnome/Tetravex/score-overlay-entry.ui")]
+private class ScoreOverlayEntry : Grid
+{
+ [GtkChild] private Label place_label;
+ [GtkChild] private Label value_label;
+
+ internal void set_place_label (string label)
+ {
+ place_label.set_label (label);
+ }
+
+ internal void set_value_label (string? label, bool bold_label = false)
+ {
+ if (label != null)
+ {
+ value_label.set_label ((!) label);
+ value_label.get_style_context ().remove_class ("italic-label");
+ }
+ else
+ {
+ /* Translators: text of the score overlay, displayed after a puzzle is complete; appears if the
player has made one of the worst scores for a game of this size; says that the rank after the one of the game
is "free", inviting to do worse */
+ value_label.set_label (_("Free!"));
+ value_label.get_style_context ().add_class ("italic-label");
+ }
+
+ if (bold_label)
+ value_label.get_style_context ().add_class ("bold-label");
+ else
+ value_label.get_style_context ().remove_class ("bold-label");
+ }
+}
diff --git a/src/tetravex.css b/src/tetravex.css
new file mode 100644
index 0000000..cdf373a
--- /dev/null
+++ b/src/tetravex.css
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 Arnaud Bonatti
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 2 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+ .score-title {
+ font-weight:bolder;
+
+ font-size:200%;
+ margin-bottom:1.5rem;
+ margin-top:0.5rem;
+
+ transition:font-size 0.5s,
+ margin-bottom 0.5s,
+ margin-top 0.5s;
+}
+.small-window .score-title {
+ font-size:150%;
+ margin-bottom:1rem;
+ margin-top:0.2rem;
+}
+
+grid.score-overlay button {
+ margin-top:0.5rem;
+}
+
+.bold-label {
+ font-weight:bolder;
+}
+
+.italic-label {
+ font-style:italic;
+}
+
+button.rounded-button {
+ border-radius: 9999px;
+ -gtk-outline-radius: 9999px;
+}
diff --git a/src/theme-extrusion.vala b/src/theme-extrusion.vala
index 0fd6838..3400588 100644
--- a/src/theme-extrusion.vala
+++ b/src/theme-extrusion.vala
@@ -104,6 +104,7 @@ private class ExtrusionTheme : Theme
\*/
private uint size = 0;
+ private uint8 animation_level = 0;
/* arrow */
private double arrow_half_h;
@@ -177,6 +178,11 @@ private class ExtrusionTheme : Theme
size = new_size;
}
+ internal override void set_animation_level (uint8 new_animation_level /* 0-16 */)
+ {
+ animation_level = new_animation_level;
+ }
+
/*\
* * drawing arrow
\*/
@@ -190,7 +196,10 @@ private class ExtrusionTheme : Theme
context.line_to (arrow_w, neg_arrow_half_h);
context.close_path ();
- context.set_source_rgba (0.5, 0.5, 0.5, 0.4);
+ if (animation_level == 0)
+ context.set_source_rgba (0.5, 0.5, 0.5, 0.4);
+ else
+ context.set_source_rgba (0.5, 0.5, 0.5, 0.4 * (16.0 - (double) animation_level) / 16.0);
context.fill ();
}
@@ -202,7 +211,10 @@ private class ExtrusionTheme : Theme
{
context.save ();
- context.set_source_rgba (0.5, 0.5, 0.5, 0.3);
+ if (animation_level == 0)
+ context.set_source_rgba (0.5, 0.5, 0.5, 0.3);
+ else
+ context.set_source_rgba (0.5, 0.5, 0.5, 0.3 * (16.0 - (double) animation_level) / 16.0);
rounded_square (context,
/* x and y */ tile_margin, tile_margin,
/* size */ tile_size,
diff --git a/src/theme-neoretro.vala b/src/theme-neoretro.vala
index 069cf22..07fa149 100644
--- a/src/theme-neoretro.vala
+++ b/src/theme-neoretro.vala
@@ -126,6 +126,7 @@ private class NeoRetroTheme : Theme
\*/
private uint size = 0;
+ private uint8 animation_level = 0;
/* arrow */
private double arrow_half_h;
@@ -138,6 +139,9 @@ private class NeoRetroTheme : Theme
private double arrow_clip_w;
private double arrow_clip_h;
+ private double arrow_border_opacity;
+ private double arrow_fill_opacity;
+
/* socket */
private uint socket_margin;
private int socket_size;
@@ -180,18 +184,7 @@ private class NeoRetroTheme : Theme
socket_margin = uint.min ((uint) (new_size * 0.05), 2);
socket_size = (int) new_size - (int) socket_margin * 2;
- socket_pattern = new Cairo.MeshPattern ();
- socket_pattern.begin_patch ();
- socket_pattern.move_to (0.0, 0.0);
- socket_pattern.line_to (1.0, 0.0);
- socket_pattern.line_to (1.0, 1.0);
- socket_pattern.line_to (0.0, 1.0);
- socket_pattern.set_corner_color_rgba (0, 0.3, 0.3, 0.3, 0.3);
- socket_pattern.set_corner_color_rgba (1, 0.4, 0.4, 0.4, 0.3);
- socket_pattern.set_corner_color_rgba (2, 0.7, 0.7, 0.7, 0.3);
- socket_pattern.set_corner_color_rgba (3, 0.6, 0.6, 0.6, 0.3);
- socket_pattern.end_patch ();
- socket_pattern.set_matrix (matrix);
+ init_socket_pattern ();
/* tile */
tile_margin = uint.min ((uint) (new_size * 0.05), 2) - 1;
@@ -209,6 +202,30 @@ private class NeoRetroTheme : Theme
size = new_size;
}
+ internal override void set_animation_level (uint8 new_animation_level /* 0-16 */)
+ {
+ animation_level = new_animation_level;
+ arrow_border_opacity = animation_level == 0 ? 0.3 : 0.3 * (16.0 - (double) animation_level) / 16.0;
+ arrow_fill_opacity = animation_level == 0 ? 0.1 : 0.1 * (16.0 - (double) animation_level) / 16.0;
+ init_socket_pattern ();
+ }
+
+ private void init_socket_pattern ()
+ {
+ socket_pattern = new Cairo.MeshPattern ();
+ socket_pattern.begin_patch ();
+ socket_pattern.move_to (0.0, 0.0);
+ socket_pattern.line_to (1.0, 0.0);
+ socket_pattern.line_to (1.0, 1.0);
+ socket_pattern.line_to (0.0, 1.0);
+ socket_pattern.set_corner_color_rgba (0, 0.3, 0.3, 0.3, arrow_border_opacity);
+ socket_pattern.set_corner_color_rgba (1, 0.4, 0.4, 0.4, arrow_border_opacity);
+ socket_pattern.set_corner_color_rgba (2, 0.7, 0.7, 0.7, arrow_border_opacity);
+ socket_pattern.set_corner_color_rgba (3, 0.6, 0.6, 0.6, arrow_border_opacity);
+ socket_pattern.end_patch ();
+ socket_pattern.set_matrix (matrix);
+ }
+
/*\
* * drawing arrow
\*/
@@ -252,11 +269,11 @@ private class NeoRetroTheme : Theme
context.set_line_cap (Cairo.LineCap.ROUND);
context.set_line_width (14.0);
- context.set_source_rgba (0.4, 0.4, 0.4, 0.3); // fill color 1, including border
+ context.set_source_rgba (0.4, 0.4, 0.4, arrow_border_opacity); // fill color 1, including border
context.stroke_preserve ();
context.set_line_width (12.0);
- context.set_source_rgba (1.0, 1.0, 1.0, 0.1); // fill color 2
+ context.set_source_rgba (1.0, 1.0, 1.0, arrow_fill_opacity); // fill color 2
context.stroke_preserve ();
/* filling interior */
@@ -264,10 +281,10 @@ private class NeoRetroTheme : Theme
context.reset_clip (); // forget the border clip
context.clip (); // clip to the current path
- context.set_source_rgba (0.4, 0.4, 0.4, 0.3); // fill color 1
+ context.set_source_rgba (0.4, 0.4, 0.4, arrow_border_opacity); // fill color 1
context.fill_preserve ();
- context.set_source_rgba (1.0, 1.0, 1.0, 0.1); // fill color 2
+ context.set_source_rgba (1.0, 1.0, 1.0, arrow_fill_opacity); // fill color 2
context.fill ();
}
@@ -288,7 +305,7 @@ private class NeoRetroTheme : Theme
context.fill_preserve ();
context.set_line_width (1.0);
- context.set_source_rgba (0.4, 0.4, 0.4, 0.3);
+ context.set_source_rgba (0.4, 0.4, 0.4, arrow_border_opacity);
context.stroke ();
context.restore ();
diff --git a/src/theme-nostalgia.vala b/src/theme-nostalgia.vala
index 9a16d39..e03375b 100644
--- a/src/theme-nostalgia.vala
+++ b/src/theme-nostalgia.vala
@@ -74,11 +74,12 @@ private class NostalgiaTheme : Theme
\*/
private uint size = 0;
+ private uint8 animation_level = 0;
/* arrow */
private double arrow_half_h;
private double neg_arrow_half_h;
- private uint arrow_depth;
+ private double arrow_depth;
private double arrow_dx;
private double arrow_dy;
private double neg_arrow_dy;
@@ -87,7 +88,7 @@ private class NostalgiaTheme : Theme
private double arrow_w_minus_depth;
/* tile and socket */
- private uint tile_depth;
+ private double tile_depth;
private double size_minus_tile_depth;
private double size_minus_two_tile_depths; // socket only
@@ -112,21 +113,8 @@ private class NostalgiaTheme : Theme
if (size != 0 && size == new_size)
return;
- /* arrow */
- arrow_half_h = new_size * 0.75;
- neg_arrow_half_h = -arrow_half_h;
- arrow_depth = uint.min ((uint) (new_size * 0.025), 2);
- arrow_dx = 1.4142 * arrow_depth;
- arrow_dy = arrow_half_h - 6.1623 * arrow_depth;
- neg_arrow_dy = -arrow_dy;
- arrow_w = new_size * PuzzleView.gap_factor * 0.5;
- arrow_x = (new_size * PuzzleView.gap_factor - arrow_w) * 0.5;
- arrow_w_minus_depth = arrow_w - arrow_depth;
-
- /* socket and tile */
- tile_depth = uint.min ((uint) (new_size * 0.05), 4);
- size_minus_tile_depth = (double) new_size - tile_depth;
- size_minus_two_tile_depths = (double) (new_size - tile_depth * 2);
+ configure_arrow (new_size);
+ configure_socket (new_size);
/* tiles */
tile_dx = 2.4142 * tile_depth;
@@ -148,6 +136,35 @@ private class NostalgiaTheme : Theme
size = new_size;
}
+ internal override void set_animation_level (uint8 new_animation_level /* 0-16 */)
+ {
+ animation_level = new_animation_level;
+ configure_arrow (size);
+ configure_socket (size);
+ }
+
+ private void configure_arrow (uint new_size)
+ {
+ arrow_half_h = new_size * 0.75;
+ neg_arrow_half_h = -arrow_half_h;
+ arrow_depth = double.min (new_size * 0.025, 2.0) - (double) animation_level / 6.0;
+ arrow_depth = double.max (arrow_depth, 0.0);
+ arrow_dx = 1.4142 * arrow_depth;
+ arrow_dy = arrow_half_h - 6.1623 * arrow_depth;
+ neg_arrow_dy = -arrow_dy;
+ arrow_w = new_size * PuzzleView.gap_factor * 0.5;
+ arrow_x = (new_size * PuzzleView.gap_factor - arrow_w) * 0.5;
+ arrow_w_minus_depth = arrow_w - arrow_depth;
+ }
+
+ private void configure_socket (uint new_size)
+ {
+ tile_depth = double.min (new_size * 0.05, 4.0) - (double) animation_level / 4.0;
+ tile_depth = double.max (tile_depth, 0.0);
+ size_minus_tile_depth = (double) new_size - tile_depth;
+ size_minus_two_tile_depths = (double) new_size - tile_depth * 2.0;
+ }
+
/*\
* * drawing arrow
\*/
@@ -161,7 +178,10 @@ private class NostalgiaTheme : Theme
context.line_to (arrow_w, arrow_half_h);
context.line_to (arrow_w, neg_arrow_half_h);
context.close_path ();
- context.set_source_rgba (0.0, 0.0, 0.0, 0.125);
+ if (animation_level == 0)
+ context.set_source_rgba (0, 0, 0, 0.125);
+ else
+ context.set_source_rgba (0, 0, 0, 0.125 * (16.0 - (double) animation_level) / 16.0);
context.fill ();
/* Arrow highlight */
@@ -193,7 +213,10 @@ private class NostalgiaTheme : Theme
{
/* Background */
context.rectangle (tile_depth, tile_depth, size_minus_two_tile_depths, size_minus_two_tile_depths);
- context.set_source_rgba (0.0, 0.0, 0.0, 0.125);
+ if (animation_level == 0)
+ context.set_source_rgba (0, 0, 0, 0.125);
+ else
+ context.set_source_rgba (0, 0, 0, 0.125 * (16.0 - (double) animation_level) / 16.0);
context.fill ();
/* Shadow */
diff --git a/src/theme-synesthesia.vala b/src/theme-synesthesia.vala
index 7579ece..f9cfc91 100644
--- a/src/theme-synesthesia.vala
+++ b/src/theme-synesthesia.vala
@@ -61,6 +61,7 @@ private class SynesthesiaTheme : Theme
\*/
private uint size = 0;
+ private uint8 animation_level = 0;
/* arrow */
private double arrow_half_h;
@@ -107,19 +108,7 @@ private class SynesthesiaTheme : Theme
tile_depth = uint.min ((uint) (new_size * 0.05), 4);
size_minus_tile_depth = (double) new_size - tile_depth;
size_minus_two_tile_depths = (double) (new_size - tile_depth * 2);
-
- socket_pattern = new Cairo.MeshPattern ();
- socket_pattern.begin_patch ();
- socket_pattern.move_to (0.5, 0.0);
- socket_pattern.line_to (1.0, 0.5);
- socket_pattern.line_to (0.5, 1.0);
- socket_pattern.line_to (0.0, 0.5);
- socket_pattern.set_corner_color_rgba (0, 0.45, 0.45, 0.45, 0.5);
- socket_pattern.set_corner_color_rgba (1, 0.6 , 0.6 , 0.6 , 0.5);
- socket_pattern.set_corner_color_rgba (2, 0.7 , 0.7 , 0.7 , 0.5);
- socket_pattern.set_corner_color_rgba (3, 0.55, 0.55, 0.55, 0.5);
- socket_pattern.end_patch ();
- socket_pattern.set_matrix (matrix);
+ init_socket_pattern ();
/* tile */
tile_margin = uint.min ((uint) (new_size * 0.05), 2) - 1;
@@ -138,6 +127,38 @@ private class SynesthesiaTheme : Theme
size = new_size;
}
+ internal override void set_animation_level (uint8 new_animation_level /* 0-16 */)
+ {
+ animation_level = new_animation_level;
+ init_socket_pattern ();
+ }
+
+ private void init_socket_pattern ()
+ {
+ socket_pattern = new Cairo.MeshPattern ();
+ socket_pattern.begin_patch ();
+ socket_pattern.move_to (0.5, 0.0);
+ socket_pattern.line_to (1.0, 0.5);
+ socket_pattern.line_to (0.5, 1.0);
+ socket_pattern.line_to (0.0, 0.5);
+ if (animation_level == 0)
+ {
+ socket_pattern.set_corner_color_rgba (0, 0.45, 0.45, 0.45, 0.5);
+ socket_pattern.set_corner_color_rgba (1, 0.6 , 0.6 , 0.6 , 0.5);
+ socket_pattern.set_corner_color_rgba (2, 0.7 , 0.7 , 0.7 , 0.5);
+ socket_pattern.set_corner_color_rgba (3, 0.55, 0.55, 0.55, 0.5);
+ }
+ else
+ {
+ socket_pattern.set_corner_color_rgba (0, 0.45, 0.45, 0.45, 0.5 * (16.0 - (double)
animation_level) / 16.0);
+ socket_pattern.set_corner_color_rgba (1, 0.6 , 0.6 , 0.6 , 0.5 * (16.0 - (double)
animation_level) / 16.0);
+ socket_pattern.set_corner_color_rgba (2, 0.7 , 0.7 , 0.7 , 0.5 * (16.0 - (double)
animation_level) / 16.0);
+ socket_pattern.set_corner_color_rgba (3, 0.55, 0.55, 0.55, 0.5 * (16.0 - (double)
animation_level) / 16.0);
+ }
+ socket_pattern.end_patch ();
+ socket_pattern.set_matrix (matrix);
+ }
+
/*\
* * drawing arrow
\*/
@@ -150,7 +171,10 @@ private class SynesthesiaTheme : Theme
context.line_to (arrow_w, arrow_half_h);
context.line_to (arrow_w, neg_arrow_half_h);
context.close_path ();
- context.set_source_rgba (0.5, 0.5, 0.6, 0.4);
+ if (animation_level == 0)
+ context.set_source_rgba (0.5, 0.5, 0.6, 0.4);
+ else
+ context.set_source_rgba (0.5, 0.5, 0.6, 0.4 * (16.0 - (double) animation_level) / 16.0);
context.fill ();
}
@@ -173,7 +197,10 @@ private class SynesthesiaTheme : Theme
context.fill_preserve ();
context.set_line_width (1.0);
- context.set_source_rgba (0.4, 0.4, 0.4, 0.3);
+ if (animation_level == 0)
+ context.set_source_rgba (0.4, 0.4, 0.4, 0.3);
+ else
+ context.set_source_rgba (0.4, 0.4, 0.4, 0.3 * (16.0 - (double) animation_level) / 16.0);
context.stroke ();
context.restore ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]