[gnome-taquin] Import code from Dconf Editor.
- From: Arnaud B. <arnaudb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-taquin] Import code from Dconf Editor.
- Date: Mon, 21 Jan 2019 06:11:59 +0000 (UTC)
commit 75d196f5d27c62b2b1174315608b2a434ecbd0c8
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date: Fri Jan 18 15:51:55 2019 +0100
Import code from Dconf Editor.
Import BaseWindow from commit 646cda4e76.
data/adaptative-window.ui | 27 +++
data/base-headerbar.ui | 130 +++++++++++
data/base-view.ui | 38 ++++
data/base-window.css | 131 +++++++++++
data/base-window.ui | 30 +++
data/notifications-revealer.ui | 69 ++++++
data/overlayed-list.ui | 80 +++++++
data/registry-placeholder.ui | 48 ++++
src/about-list.vala | 283 ++++++++++++++++++++++++
src/adaptative-window.vala | 428 +++++++++++++++++++++++++++++++++++
src/base-headerbar.vala | 309 ++++++++++++++++++++++++++
src/base-view.vala | 141 ++++++++++++
src/base-window.vala | 478 ++++++++++++++++++++++++++++++++++++++++
src/meson.build | 10 +
src/night-light-monitor.vala | 222 +++++++++++++++++++
src/notifications-revealer.vala | 80 +++++++
src/overlayed-list.vala | 385 ++++++++++++++++++++++++++++++++
src/registry-placeholder.vala | 54 +++++
src/taquin-main.vala | 48 ++++
src/taquin.gresource.xml | 8 +
20 files changed, 2999 insertions(+)
---
diff --git a/data/adaptative-window.ui b/data/adaptative-window.ui
new file mode 100644
index 0000000..555bf77
--- /dev/null
+++ b/data/adaptative-window.ui
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+-->
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <template class="AdaptativeWindow" parent="GtkApplicationWindow">
+ <property name="height-request">283</property> <!-- 294px max for Purism Librem 5 landscape, for 720px
width -->
+ <property name="width-request">349</property> <!-- 360px max for Purism Librem 5 portrait, for 654px
height -->
+ <signal name="window-state-event" handler="on_window_state_event"/>
+ <signal name="size-allocate" handler="on_size_allocate"/>
+ <signal name="destroy" handler="on_destroy"/>
+ </template>
+</interface>
diff --git a/data/base-headerbar.ui b/data/base-headerbar.ui
new file mode 100644
index 0000000..7f638a3
--- /dev/null
+++ b/data/base-headerbar.ui
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+-->
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <template class="BaseHeaderBar" parent="NightTimeAwareHeaderBar">
+ <property name="show-close-button">True</property>
+ <property name="has-subtitle">False</property>
+ <child>
+ <object class="GtkButton" id="go_back_button">
+ <property name="visible">False</property>
+ <property name="valign">center</property>
+ <property name="action-name">base.show-default-view</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">go-previous-symbolic</property> <!-- TODO 3/3
dialog-information-symbolic -->
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparator" id="ltr_left_separator">
+ <property name="visible">False</property>
+ </object>
+ </child>
+ <child type="title">
+ <object class="GtkBox" id="center_box">
+ <property name="visible">True</property>
+ <property name="valign">fill</property>
+ <property name="spacing">6</property>
+ <property name="hexpand">False</property> <!-- or "hexpand-set">true... that's horrible -->
+ <style>
+ <class name="centerwidget"/>
+ </style>
+ <child>
+ <object class="GtkLabel" id="title_label">
+ <property name="visible">False</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStack" id="quit_button_stack">
+ <property name="visible">False</property>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="action-name">app.quit</property>
+ <style>
+ <class name="titlebutton"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">window-close-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="name">quit-button</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator" id="ltr_right_separator">
+ <property name="visible">False</property>
+ <property name="orientation">vertical</property>
+ <style>
+ <class name="titlebutton"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="info_button">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="focus-on-click">False</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child internal-child="accessible">
+ <object class="AtkObject">
+ <!-- Translators: accessible name of the hamburger menu -->
+ <property name="AtkObject::accessible-name" translatable="yes">Actions</property>
+ <!-- Translators: accessible description of the hamburger menu -->
+ <property name="AtkObject::accessible-description" translatable="yes">Current view
actions</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">open-menu-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ </template>
+</interface>
diff --git a/data/base-view.ui b/data/base-view.ui
new file mode 100644
index 0000000..850cf9b
--- /dev/null
+++ b/data/base-view.ui
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+-->
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <template class="BaseView" parent="GtkStack">
+ <property name="transition-type">over-down-up</property>
+ <property name="transition-duration">300</property>
+ <child>
+ <object class="GtkOverlay" id="notifications_overlay"> <!-- or "main-view", see packing -->
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkGrid" id="main_grid">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="name">main-view</property>
+ </packing>
+ </child>
+ </template>
+</interface>
diff --git a/data/base-window.css b/data/base-window.css
new file mode 100644
index 0000000..c601e75
--- /dev/null
+++ b/data/base-window.css
@@ -0,0 +1,131 @@
+/*
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+/*\
+* * center widget revealers
+\*/
+
+/* hack: fix the double space when the child of the revealer is hidden; 6px is the spacing of the
centerwidget box */
+.centerwidget:dir(ltr) .headerbar-revealer { padding-right:6px; }
+.centerwidget:dir(rtl) .headerbar-revealer { padding-left :6px; }
+
+.centerwidget:dir(ltr) { margin-right:-6px; }
+.centerwidget:dir(rtl) { margin-left :-6px; }
+
+/*\
+* * app notification
+\*/
+
+.extra-thin-window .app-notification,
+ .thin-window .app-notification {
+ border-radius:0;
+}
+
+/*\
+* * overlayed list
+\*/
+
+ .vertical.overlayed-list.needs-padding > scrolledwindow > viewport > list {
+ padding-bottom:0;
+ transition:padding-bottom 0.3s,
+ font-size 0.3s;
+}
+ .vertical.overlayed-list.needs-padding > scrolledwindow > viewport > list:not(.empty-list) {
+ padding-bottom:3rem;
+}
+ .vertical.overlayed-list.needs-padding > .linked-circular {
+ margin-bottom:0.3rem;
+}
+
+.horizontal.overlayed-list:dir(ltr).needs-padding > scrolledwindow > viewport > list > row {
+ padding-right:calc(160px+1rem); /* width-request of edit_mode_box + a bit */
+}
+.horizontal.overlayed-list:dir(rtl).needs-padding > scrolledwindow > viewport > list > row {
+ padding-left :calc(160px+1rem); /* width-request of edit_mode_box + a bit */
+}
+.horizontal.overlayed-list:dir(ltr).needs-padding > .linked-circular {
+ margin-right:1.5rem;
+}
+.horizontal.overlayed-list:dir(rtl).needs-padding > .linked-circular {
+ margin-left :1.5rem;
+}
+
+ .vertical.overlayed-list:dir(rtl) > .linked-circular > .right-on-ltr,
+ .vertical.overlayed-list:dir(ltr) > .linked-circular > .left-on-ltr {
+ border-radius:2rem 0 0 2rem;
+ -gtk-outline-top-left-radius :2rem;
+ -gtk-outline-bottom-left-radius:2rem;
+}
+
+ .vertical.overlayed-list:dir(ltr) > .linked-circular > .right-on-ltr,
+ .vertical.overlayed-list:dir(rtl) > .linked-circular > .left-on-ltr {
+ border-radius:0 2rem 2rem 0;
+ -gtk-outline-top-right-radius :2rem;
+ -gtk-outline-bottom-right-radius:2rem;
+}
+
+.horizontal.overlayed-list:dir(rtl) > .linked-circular > .right-on-ltr,
+.horizontal.overlayed-list:dir(ltr) > .linked-circular > .left-on-ltr {
+ border-radius:1.5rem 1.5rem 0 0;
+ -gtk-outline-top-right-radius:1.4rem;
+ -gtk-outline-top-left-radius :1.4rem;
+}
+.horizontal.overlayed-list:dir(ltr) > .linked-circular > .right-on-ltr,
+.horizontal.overlayed-list:dir(rtl) > .linked-circular > .left-on-ltr {
+ border-radius:0 0 1.5rem 1.5rem;
+ -gtk-outline-bottom-right-radius:1.4rem;
+ -gtk-outline-bottom-left-radius :1.4rem;
+}
+
+/*\
+* * in-window about
+\*/
+
+.about-list > scrolledwindow > viewport > list > row {
+ min-height:1.3rem;
+}
+
+.about-list > scrolledwindow > viewport > list > row > label {
+ margin:4px;
+}
+
+.about-list > scrolledwindow > viewport > list > row > button {
+ margin-top:2px;
+ margin-bottom:2px;
+}
+
+/*\
+* * text formating
+\*/
+
+.small-label {
+ font-size:small;
+}
+.bold-label {
+ font-weight:bold;
+}
+.italic-label {
+ font-style:italic;
+}
+
+.big-popover {
+ font-size:144%;
+}
+
+/*\
+* * the end
+\*/
diff --git a/data/base-window.ui b/data/base-window.ui
new file mode 100644
index 0000000..22e39e3
--- /dev/null
+++ b/data/base-window.ui
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+-->
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <template class="BaseWindow" parent="AdaptativeWindow">
+ <property name="visible">False</property>
+ <signal name="key-press-event" handler="on_key_press_event"/>
+ <child>
+ <object class="GtkGrid" id="main_grid">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/notifications-revealer.ui b/data/notifications-revealer.ui
new file mode 100644
index 0000000..9769665
--- /dev/null
+++ b/data/notifications-revealer.ui
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+-->
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <template class="NotificationsRevealer" parent="GtkRevealer">
+ <property name="halign">center</property>
+ <property name="valign">start</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="visible">True</property>
+ <style>
+ <class name="app-notification"/>
+ </style>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="column-spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="notification_label">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <property name="focus-on-click">False</property>
+ <property name="relief">none</property>
+ <property name="action-name">notification.hide</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">window-close-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/overlayed-list.ui b/data/overlayed-list.ui
new file mode 100644
index 0000000..61f7d80
--- /dev/null
+++ b/data/overlayed-list.ui
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+-->
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <template class="OverlayedList" parent="GtkOverlay">
+ <property name="expand">True</property>
+ <property name="valign">fill</property>
+ <style>
+ <class name="overlayed-list"/>
+ <class name="vertical"/>
+ </style>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolled">
+ <property name="visible">True</property>
+ <property name="expand">True</property>
+ <property name="hscrollbar-policy">never</property>
+ <property name="max-content-height">300</property>
+ <property name="propagate-natural-width">True</property>
+ <property name="propagate-natural-height">True</property>
+ <child>
+ <object class="GtkListBox" id="main_list_box">
+ <property name="visible">True</property>
+ <signal name="selected-rows-changed" handler="on_selection_changed"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="GtkBox" id="edit_mode_box">
+ <property name="visible">False</property>
+ <property name="halign">center</property>
+ <property name="valign">end</property>
+ <property name="width-request">200</property>
+ <style>
+ <class name="linked"/>
+ <class name="linked-circular"/>
+ </style>
+ <child>
+ <object class="GtkModelButton" id="leave_edit_mode_button">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="centered">True</property>
+ <property name="iconic">True</property>
+ <property name="focus-on-click">False</property>
+ <style>
+ <class name="left-on-ltr"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkModelButton" id="enter_edit_mode_button">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="centered">True</property>
+ <property name="iconic">True</property>
+ <property name="focus-on-click">False</property>
+ <style>
+ <class name="right-on-ltr"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/registry-placeholder.ui b/data/registry-placeholder.ui
new file mode 100644
index 0000000..2be926f
--- /dev/null
+++ b/data/registry-placeholder.ui
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+-->
+<interface>
+ <requires lib="gtk+" version="3.12"/>
+ <template class="RegistryPlaceholder" parent="GtkGrid"> <!-- see
nautilus/src/resources/ui/nautilus-folder-is-empty.ui -->
+ <property name="visible">True</property>
+ <property name="row-spacing">12</property>
+ <property name="expand">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="orientation">vertical</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <child>
+ <object class="GtkImage" id="placeholder_image">
+ <property name="visible">True</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="placeholder_label">
+ <property name="visible">True</property>
+ <property name="justify">center</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/about-list.vala b/src/about-list.vala
new file mode 100644
index 0000000..bbce2a5
--- /dev/null
+++ b/src/about-list.vala
@@ -0,0 +1,283 @@
+/*
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+private class AboutList : OverlayedList
+{
+ // same as about dialog; skipped: wrap_license, license, logo
+ public string [] artists { private get; protected construct; default = {}; }
+ public string [] authors { private get; protected construct; default = {}; }
+ public string comments { private get; protected construct; default = ""; }
+ public string copyright { private get; protected construct; default = ""; }
+ public string [] documenters { private get; protected construct; default = {}; }
+ private License license_type { private get { return License.GPL_3_0; }} // forced, 2/3
+ public string logo_icon_name { private get; protected construct; default = ""; }
+ public string program_name { private get; protected construct; default = ""; }
+ public string translator_credits { private get; protected construct; default = ""; }
+ public string version { private get; protected construct; default = ""; }
+ public string website { private get; protected construct; default = ""; }
+ public string website_label { private get; protected construct; default = ""; }
+
+ construct
+ {
+ install_action_entries ();
+
+ main_list_box.selection_mode = SelectionMode.NONE;
+ get_style_context ().add_class ("about-list");
+
+ /* Translators: on really small windows, the about dialog is replaced by an in-window view; here is
the label of one of two buttons that have the same role as the stack switcher in the usual about dialog, the
second is "Credits" */
+ first_mode_name = _("About");
+ /* Translators: on really small windows, the about dialog is replaced by an in-window view; here is
the label of one of two buttons that have the same role as the stack switcher in the usual about dialog, the
first is "About" */
+ second_mode_name = _("Credits");
+ change_editability (true);
+
+ show_apropos (ref main_list_store);
+ }
+
+ internal AboutList (bool needs_shadows,
+ bool big_placeholder,
+ ref string [] artists,
+ ref string [] authors,
+ ref string comments,
+ ref string copyright,
+ ref string [] documenters,
+ ref string logo_icon_name,
+ ref string program_name,
+ ref string translator_credits,
+ ref string version,
+ ref string website,
+ ref string website_label)
+ {
+ Object (needs_shadows : needs_shadows,
+ big_placeholder : big_placeholder,
+ edit_mode_action_prefix : "about",
+ artists: artists, authors: authors, comments: comments, copyright: copyright, documenters:
documenters, logo_icon_name: logo_icon_name, program_name: program_name, translator_credits:
translator_credits, version: version, website: website, website_label: website_label);
+ }
+
+ internal override void reset ()
+ {
+ edit_mode_action.set_state (false);
+ show_apropos (ref main_list_store);
+ scroll_top ();
+ }
+
+ /*\
+ * * Action entries
+ \*/
+
+ SimpleAction edit_mode_action;
+
+ private void install_action_entries ()
+ {
+ SimpleActionGroup action_group = new SimpleActionGroup ();
+ action_group.add_action_entries (action_entries, this);
+ insert_action_group ("about", action_group);
+
+ edit_mode_action = (SimpleAction) action_group.lookup_action ("set-edit-mode");
+ }
+
+ private const GLib.ActionEntry [] action_entries =
+ {
+ { "set-edit-mode", set_edit_mode, "b", "false" }
+ };
+
+ private void set_edit_mode (SimpleAction action, Variant? variant)
+ requires (variant != null)
+ {
+ bool new_state = ((!) variant).get_boolean ();
+ action.set_state (new_state);
+
+ if (new_state)
+ show_credits (ref main_list_store);
+ else
+ show_apropos (ref main_list_store);
+ }
+
+ private inline void show_apropos (ref GLib.ListStore main_list_store)
+ {
+ main_list_store.remove_all ();
+ if (program_name == "")
+ assert_not_reached ();
+ if (logo_icon_name != "")
+ main_list_store.append (new AboutListItem.from_icon_name (logo_icon_name, program_name));
+ AboutListItem program = new AboutListItem.from_label (program_name, "bold-label");
+ main_list_store.append (program);
+
+ if (version != "")
+ main_list_store.append (new AboutListItem.from_label (version));
+ if (comments != "")
+ main_list_store.append (new AboutListItem.from_label (comments));
+ if (website != "")
+ main_list_store.append (new AboutListItem.from_link (website, website_label)); //
website_label can be empty
+ if (copyright != "")
+ main_list_store.append (new AboutListItem.from_label (copyright, "small-label"));
+
+ // forced, 3/3 // TODO support all licenses type
+ if (license_type != License.GPL_3_0)
+ assert_not_reached ();
+
+ /* Translators: on really small windows, the about dialog is replaced by an in-window view; here is
the label of the link to the GPL license; TODO better text, as in the usual about dialog */
+ main_list_store.append (new AboutListItem.from_link
("https://www.gnu.org/licenses/gpl-3.0.html", _("GNU General Public License\nversion 3 or later")));
+
+ program.grab_focus ();
+ }
+
+ private inline void show_credits (ref GLib.ListStore main_list_store)
+ {
+ main_list_store.remove_all ();
+ if (program_name == "")
+ assert_not_reached ();
+ if (logo_icon_name != "")
+ main_list_store.append (new AboutListItem.from_icon_name (logo_icon_name, program_name));
+ AboutListItem program = new AboutListItem.from_label (program_name, "bold-label");
+ main_list_store.append (program);
+
+ if (authors.length > 0)
+ {
+ string authors_string = get_multiline_string_from_string_array (authors);
+ /* Translators: on really small windows, the about dialog is replaced by an in-window view; here
is the header of the programmers names */
+ main_list_store.append (new AboutListItem.with_title (authors_string, _("Creators")));
+ }
+
+ if (documenters.length > 0)
+ {
+ string documenters_string = get_multiline_string_from_string_array (documenters);
+ /* Translators: on really small windows, the about dialog is replaced by an in-window view; here
is the header of the documenters names */
+ main_list_store.append (new AboutListItem.with_title (documenters_string,
_("Documenters")));
+ }
+
+ if (translator_credits != "")
+ /* Translators: on really small windows, the about dialog is replaced by an in-window view; here
is the header of the translators names */
+ main_list_store.append (new AboutListItem.with_title (translator_credits,
_("Translators")));
+
+ if (artists.length > 0)
+ {
+ string artists_string = get_multiline_string_from_string_array (artists);
+ /* Translators: on really small windows, the about dialog is replaced by an in-window view; here
is the header of the pictural artists names */
+ main_list_store.append (new AboutListItem.with_title (artists_string, _("Artists")));
+ }
+
+ program.grab_focus ();
+ }
+ private static string get_multiline_string_from_string_array (string [] string_array)
+ {
+ if (string_array.length <= 0)
+ return "";
+ string multiline_string = "";
+ uint position = 0;
+ uint max_position = string_array.length - 1;
+ foreach (string string_item in string_array)
+ {
+ multiline_string += string_item;
+ if (position < max_position)
+ multiline_string += "\n";
+ position++;
+ }
+ return multiline_string;
+ }
+}
+
+private class AboutListItem : OverlayedListRow
+{
+ [CCode (notify = false)] public string copy_text { internal get; construct; default = ""; }
+
+ internal override bool handle_copy_text (out string text)
+ {
+ text = copy_text;
+ return true;
+ }
+
+ internal AboutListItem.from_label (string text, string? css_class = null)
+ {
+ Object (copy_text: text);
+
+ Label label = new Label (text);
+ label.visible = true;
+ label.hexpand = true;
+ label.wrap_mode = Pango.WrapMode.WORD_CHAR;
+ label.wrap = true;
+ label.justify = Justification.CENTER;
+ label.selectable = true;
+ label.can_focus = false;
+ if (css_class != null)
+ label.get_style_context ().add_class ((!) css_class);
+ add (label);
+ }
+
+ internal AboutListItem.from_icon_name (string icon_name, string copy_text)
+ {
+ Object (copy_text: copy_text);
+
+ Image image = new Image.from_icon_name (icon_name, IconSize.DIALOG);
+ image.pixel_size = 128;
+ image.visible = true;
+ image.hexpand = true;
+ add (image);
+ }
+
+ internal AboutListItem.from_link (string link, string text_or_empty) // TODO do not allow button focus,
and activate it on row activation
+ {
+ Object (copy_text: link);
+
+ LinkButton button;
+ if (text_or_empty == "")
+ button = new LinkButton (link);
+ else
+ button = new LinkButton.with_label (link, text_or_empty);
+ button.visible = true;
+ button.hexpand = true;
+
+ Widget? widget = button.get_child ();
+ if (widget == null || !(((!) widget) is Label))
+ assert_not_reached ();
+ Label label = (Label) (!) widget;
+ label.wrap_mode = Pango.WrapMode.WORD_CHAR;
+ label.wrap = true;
+ label.justify = Justification.CENTER;
+
+ add (button);
+ }
+
+ internal AboutListItem.with_title (string text, string title)
+ {
+ Object (copy_text: text);
+
+ Grid grid = new Grid ();
+ grid.orientation = Orientation.VERTICAL;
+ grid.visible = true;
+ add (grid);
+
+ Label label = new Label (title);
+ label.visible = true;
+ label.hexpand = true;
+ label.wrap_mode = Pango.WrapMode.WORD_CHAR;
+ label.wrap = true;
+ label.get_style_context ().add_class ("bold-label");
+ grid.add (label);
+
+ label = new Label (text);
+ label.visible = true;
+ label.hexpand = true;
+ label.wrap_mode = Pango.WrapMode.WORD_CHAR;
+ label.wrap = true;
+ label.selectable = true;
+ label.get_style_context ().add_class ("small-label");
+ label.justify = Justification.CENTER;
+ grid.add (label);
+ }
+}
diff --git a/src/adaptative-window.vala b/src/adaptative-window.vala
new file mode 100644
index 0000000..2be94d6
--- /dev/null
+++ b/src/adaptative-window.vala
@@ -0,0 +1,428 @@
+/*
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+private interface AdaptativeWidget : Object
+{ /*
+ ╎ extra ╎
+ ╎ thin ╎
+ ╶╶╶╶ ┏━━━━━━━┳━━━━━━━┳━━━━━──╴
+ extra ┃ PHONE ┃ PHONE ┃ EXTRA
+ flat ┃ _BOTH ┃ _HZTL ┃ _FLAT
+ ╶╶╶╶ ┣━━━━━━━╋━━━━━━━╋━━━━╾──╴
+ ┃ PHONE ┃ ┃
+ ┃ _VERT ┃ ┃
+ ┣━━━━━━━┫ ┃
+ ┃ EXTRA ┃ QUITE ╿ USUAL
+ ╿ _THIN │ _THIN │ _SIZE
+ ╵ ╵ ╵
+ ╎ quite thin ╎
+ */
+
+ internal enum WindowSize {
+ START_SIZE,
+ USUAL_SIZE,
+ QUITE_THIN,
+ PHONE_VERT,
+ PHONE_HZTL,
+ PHONE_BOTH,
+ EXTRA_THIN,
+ EXTRA_FLAT;
+
+ internal static inline bool is_phone_size (WindowSize window_size)
+ {
+ return (window_size == PHONE_BOTH) || (window_size == PHONE_VERT) || (window_size == PHONE_HZTL);
+ }
+
+ internal static inline bool is_extra_thin (WindowSize window_size)
+ {
+ return (window_size == PHONE_BOTH) || (window_size == PHONE_VERT) || (window_size == EXTRA_THIN);
+ }
+
+ internal static inline bool is_extra_flat (WindowSize window_size)
+ {
+ return (window_size == PHONE_BOTH) || (window_size == PHONE_HZTL) || (window_size == EXTRA_FLAT);
+ }
+
+ internal static inline bool is_quite_thin (WindowSize window_size)
+ {
+ return is_extra_thin (window_size) || (window_size == PHONE_HZTL) || (window_size == QUITE_THIN);
+ }
+ }
+
+ internal abstract void set_window_size (WindowSize new_size);
+}
+
+private abstract class NightTimeAwareHeaderBar : HeaderBar
+{
+ private bool night_time = false; // no need to use NightTime here (that allows an "Unknown"
value)
+ private bool dark_theme = false;
+ private bool automatic_night_mode = false;
+
+ /*\
+ * * construct
+ \*/
+
+ [CCode (notify = false)] public NightLightMonitor night_light_monitor
+ {
+ protected construct
+ {
+ night_time = NightLightMonitor.NightTime.should_use_dark_theme (value.night_time);
+ dark_theme = value.dark_theme;
+ automatic_night_mode = value.automatic_night_mode;
+ // menu is already updated three times at startup, let's not add one
+
+ ulong night_time_handler = value.notify ["night-time"].connect (night_time_changed);
+ ulong dark_theme_handler = value.notify ["dark-theme"].connect (dark_theme_changed);
+ ulong auto_night_handler = value.notify ["automatic-night-mode"].connect
(automatic_night_mode_changed);
+
+ destroy.connect (() => {
+ value.disconnect (night_time_handler);
+ value.disconnect (dark_theme_handler);
+ value.disconnect (auto_night_handler);
+ });
+ }
+ }
+
+ private void night_time_changed (Object nlm, ParamSpec thing)
+ {
+ night_time = NightLightMonitor.NightTime.should_use_dark_theme (((NightLightMonitor)
nlm).night_time);
+ update_hamburger_menu ();
+ }
+
+ private void dark_theme_changed (Object nlm, ParamSpec thing)
+ {
+ dark_theme = ((NightLightMonitor) nlm).dark_theme;
+ update_hamburger_menu ();
+ }
+
+ private void automatic_night_mode_changed (Object nlm, ParamSpec thing)
+ {
+ automatic_night_mode = ((NightLightMonitor) nlm).automatic_night_mode;
+ // menu update not needed
+ }
+
+ /*\
+ * * high-contrast state
+ \*/
+
+ private bool highcontrast_state = false;
+
+ internal void set_highcontrast_state (bool highcontrast_new_state)
+ {
+ if (highcontrast_state == highcontrast_new_state)
+ return;
+ highcontrast_state = highcontrast_new_state;
+ update_hamburger_menu ();
+ }
+
+ /*\
+ * * hamburger menu
+ \*/
+
+ protected abstract void update_hamburger_menu ();
+
+ protected void append_or_not_night_mode_entry (ref GLib.Menu section)
+ {
+ if (night_time && !highcontrast_state)
+ append_night_mode_entry (dark_theme, automatic_night_mode, ref section);
+ }
+ private static inline void append_night_mode_entry (bool dark_theme, bool auto_night, ref GLib.Menu
section)
+ {
+ if (dark_theme)
+ /* Translators: there are three related actions: "use", "reuse" and "pause"; displayed in the
hamburger menu at night */
+ section.append (_("Pause night mode"), "app.set-use-night-mode(false)");
+
+ else if (auto_night)
+ /* Translators: there are three related actions: "use", "reuse" and "pause"; displayed in the
hamburger menu at night */
+ section.append (_("Reuse night mode"), "app.set-use-night-mode(true)");
+
+ else
+ /* Translators: there are three related actions: "use", "reuse" and "pause"; displayed in the
hamburger menu at night */
+ section.append (_("Use night mode"), "app.set-use-night-mode(true)");
+ }
+}
+
+private const int LARGE_WINDOW_SIZE = 1042;
+
+[GtkTemplate (ui = "/org/gnome/Taquin/ui/adaptative-window.ui")]
+private abstract class AdaptativeWindow : ApplicationWindow
+{
+ private NightTimeAwareHeaderBar headerbar;
+ [CCode (notify = false)] public NightTimeAwareHeaderBar nta_headerbar
+ {
+ protected get { return headerbar; }
+ protected construct
+ {
+ NightTimeAwareHeaderBar? _value = value;
+ if (_value == null)
+ assert_not_reached ();
+
+ headerbar = value;
+ value.show ();
+ set_titlebar (value);
+ }
+ }
+
+ [CCode (notify = false)] public string window_title
+ {
+ protected construct
+ {
+ string? _value = value;
+ if (_value == null)
+ assert_not_reached ();
+
+ title = value;
+ }
+ }
+
+ private StyleContext window_style_context;
+ [CCode (notify = false)] public string specific_css_class_or_empty
+ {
+ protected construct
+ {
+ string? _value = value;
+ if (_value == null)
+ assert_not_reached ();
+
+ window_style_context = get_style_context ();
+ if (value != "")
+ window_style_context.add_class (value);
+ }
+ }
+
+ construct
+ {
+ // window_style_context is created by specific_css_class_or_empty
+ window_style_context.add_class ("startup");
+
+ manage_high_contrast ();
+
+ load_window_state ();
+
+ Timeout.add (300, () => { window_style_context.remove_class ("startup"); return Source.REMOVE; });
+ }
+
+ /*\
+ * * callbacks
+ \*/
+
+ [GtkCallback]
+ private bool on_window_state_event (Widget widget, Gdk.EventWindowState event)
+ {
+ if ((event.changed_mask & Gdk.WindowState.MAXIMIZED) != 0)
+ window_is_maximized = (event.new_window_state & Gdk.WindowState.MAXIMIZED) != 0;
+ /* We don’t save this state, but track it for saving size allocation */
+ if ((event.changed_mask & Gdk.WindowState.TILED) != 0)
+ window_is_tiled = (event.new_window_state & Gdk.WindowState.TILED) != 0;
+
+ return false;
+ }
+
+ [GtkCallback]
+ private void on_size_allocate (Allocation allocation)
+ {
+ int height = allocation.height;
+ int width = allocation.width;
+
+ update_adaptative_children (ref width, ref height);
+ update_window_state ();
+ }
+
+ [GtkCallback]
+ private void on_destroy ()
+ {
+ before_destroy ();
+ save_window_state ();
+ base.destroy ();
+ }
+
+ protected virtual void before_destroy () {}
+
+ /*\
+ * * adaptative stuff
+ \*/
+
+ private AdaptativeWidget.WindowSize window_size = AdaptativeWidget.WindowSize.START_SIZE;
+
+ private List<AdaptativeWidget> adaptative_children = new List<AdaptativeWidget> ();
+ protected void add_adaptative_child (AdaptativeWidget child)
+ {
+ adaptative_children.append (child);
+ }
+
+ private void update_adaptative_children (ref int width, ref int height)
+ {
+ bool extra_flat = height < 400;
+ bool flat = height < 500;
+
+ if (width < 590)
+ {
+ if (extra_flat) change_window_size (AdaptativeWidget.WindowSize.PHONE_BOTH);
+ else if (height < 787) change_window_size (AdaptativeWidget.WindowSize.PHONE_VERT);
+ else change_window_size (AdaptativeWidget.WindowSize.EXTRA_THIN);
+
+ set_style_classes (/* extra thin */ true, /* thin */ true, /* large */ false,
+ /* extra flat */ extra_flat, /* flat */ flat);
+ }
+ else if (width < 787)
+ {
+ if (extra_flat) change_window_size (AdaptativeWidget.WindowSize.PHONE_HZTL);
+ else change_window_size (AdaptativeWidget.WindowSize.QUITE_THIN);
+
+ set_style_classes (/* extra thin */ false, /* thin */ true, /* large */ false,
+ /* extra flat */ extra_flat, /* flat */ flat);
+ }
+ else
+ {
+ if (extra_flat) change_window_size (AdaptativeWidget.WindowSize.EXTRA_FLAT);
+ else change_window_size (AdaptativeWidget.WindowSize.USUAL_SIZE);
+
+ set_style_classes (/* extra thin */ false, /* thin */ false, /* large */ (width >
LARGE_WINDOW_SIZE),
+ /* extra flat */ extra_flat, /* flat */ flat);
+ }
+ }
+
+ private void change_window_size (AdaptativeWidget.WindowSize new_window_size)
+ {
+ if (window_size == new_window_size)
+ return;
+ window_size = new_window_size;
+ adaptative_children.@foreach ((adaptative_child) => adaptative_child.set_window_size
(new_window_size));
+ }
+
+ /*\
+ * * manage style classes
+ \*/
+
+ private bool has_extra_thin_window_class = false;
+ private bool has_thin_window_class = false;
+ private bool has_large_window_class = false;
+ private bool has_extra_flat_window_class = false;
+ private bool has_flat_window_class = false;
+
+ private void set_style_classes (bool extra_thin_window, bool thin_window, bool large_window,
+ bool extra_flat_window, bool flat_window)
+ {
+ // for width
+ if (has_extra_thin_window_class && !extra_thin_window)
+ set_style_class ("extra-thin-window", false, ref has_extra_thin_window_class);
+ if (has_thin_window_class && !thin_window)
+ set_style_class ("thin-window", false, ref has_thin_window_class);
+
+ if (large_window != has_large_window_class)
+ set_style_class ("large-window", large_window, ref has_large_window_class);
+ if (thin_window != has_thin_window_class)
+ set_style_class ("thin-window", thin_window, ref has_thin_window_class);
+ if (extra_thin_window != has_extra_thin_window_class)
+ set_style_class ("extra-thin-window", extra_thin_window, ref has_extra_thin_window_class);
+
+ // for height
+ if (has_extra_flat_window_class && !extra_flat_window)
+ set_style_class ("extra-flat-window", false, ref has_extra_flat_window_class);
+
+ if (flat_window != has_flat_window_class)
+ set_style_class ("flat-window", flat_window, ref has_flat_window_class);
+ if (extra_flat_window != has_extra_flat_window_class)
+ set_style_class ("extra-flat-window", extra_flat_window, ref has_extra_flat_window_class);
+ }
+
+ private inline void set_style_class (string class_name, bool new_state, ref bool old_state)
+ {
+ old_state = new_state;
+ if (new_state)
+ window_style_context.add_class (class_name);
+ else
+ window_style_context.remove_class (class_name);
+ }
+
+ /*\
+ * * manage window state
+ \*/
+
+ private GLib.Settings settings = new GLib.Settings ("org.gnome.Taquin");
+
+ private int window_width = 0;
+ private int window_height = 0;
+ private bool window_is_maximized = false;
+ private bool window_is_tiled = false;
+
+ private void load_window_state () // called on construct
+ {
+ if (settings.get_boolean ("window-is-maximized"))
+ maximize ();
+ set_default_size (settings.get_int ("window-width"), settings.get_int ("window-height"));
+ }
+
+ private void update_window_state () // called on size-allocate
+ {
+ if (window_is_maximized || window_is_tiled)
+ return;
+ int? _window_width = null;
+ int? _window_height = null;
+ get_size (out _window_width, out _window_height);
+ if (_window_width == null || _window_height == null)
+ return;
+ window_width = (!) _window_width;
+ window_height = (!) _window_height;
+ }
+
+ private void save_window_state () // called on destroy
+ {
+ settings.delay ();
+ settings.set_int ("window-width", window_width);
+ settings.set_int ("window-height", window_height);
+ settings.set_boolean ("window-is-maximized", window_is_maximized);
+ settings.apply ();
+ }
+
+ /*\
+ * * manage high-constrast
+ \*/
+
+ private void manage_high_contrast ()
+ {
+ Gtk.Settings? nullable_gtk_settings = Gtk.Settings.get_default ();
+ if (nullable_gtk_settings == null)
+ return;
+
+ Gtk.Settings gtk_settings = (!) nullable_gtk_settings;
+ gtk_settings.notify ["gtk-theme-name"].connect (update_highcontrast_state);
+ _update_highcontrast_state (gtk_settings);
+ }
+
+ private void update_highcontrast_state (Object gtk_settings, ParamSpec unused)
+ {
+ _update_highcontrast_state ((Gtk.Settings) gtk_settings);
+ }
+
+ private bool highcontrast_state = false;
+ private void _update_highcontrast_state (Gtk.Settings gtk_settings)
+ {
+ bool highcontrast_new_state = "HighContrast" in gtk_settings.gtk_theme_name;
+ if (highcontrast_new_state == highcontrast_state)
+ return;
+ highcontrast_state = highcontrast_new_state;
+ headerbar.set_highcontrast_state (highcontrast_new_state);
+
+ if (highcontrast_new_state)
+ window_style_context.add_class ("hc-theme");
+ else
+ window_style_context.remove_class ("hc-theme");
+ }
+}
diff --git a/src/base-headerbar.vala b/src/base-headerbar.vala
new file mode 100644
index 0000000..0ee225a
--- /dev/null
+++ b/src/base-headerbar.vala
@@ -0,0 +1,309 @@
+/*
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Taquin/ui/base-headerbar.ui")]
+private class BaseHeaderBar : NightTimeAwareHeaderBar, AdaptativeWidget
+{
+ [GtkChild] protected Box center_box;
+
+ construct
+ {
+ center_box.valign = Align.FILL;
+
+ register_modes ();
+ }
+
+ /*\
+ * * properties
+ \*/
+
+ private bool has_a_phone_size = false;
+ protected bool disable_popovers = false;
+ protected bool disable_action_bar = false;
+ protected virtual void set_window_size (AdaptativeWidget.WindowSize new_size)
+ {
+ has_a_phone_size = AdaptativeWidget.WindowSize.is_phone_size (new_size);
+ disable_popovers = has_a_phone_size
+ || AdaptativeWidget.WindowSize.is_extra_thin (new_size);
+
+ bool _disable_action_bar = disable_popovers
+ || AdaptativeWidget.WindowSize.is_extra_flat (new_size);
+ if (disable_action_bar != _disable_action_bar)
+ {
+ disable_action_bar = _disable_action_bar;
+ if (disable_action_bar)
+ {
+ set_show_close_button (false);
+ quit_button_stack.show ();
+ ltr_right_separator.visible = current_mode_id == default_mode_id;
+ }
+ else
+ {
+ ltr_right_separator.hide ();
+ quit_button_stack.hide ();
+ set_show_close_button (true);
+ }
+ }
+
+ update_hamburger_menu ();
+ }
+
+ /*\
+ * * hamburger menu
+ \*/
+
+ [CCode (notify = false)] public string about_action_label { internal get; protected construct; } //
TODO add default = _("About");
+ [CCode (notify = false)] public bool has_help { private get; protected construct;
default = false; }
+ [CCode (notify = false)] public bool has_keyboard_shortcuts { private get; protected construct;
default = false; }
+
+ protected override void update_hamburger_menu ()
+ {
+ GLib.Menu menu = new GLib.Menu ();
+
+/* if (current_type == ViewType.OBJECT && !ModelUtils.is_folder_path (current_path)) // TODO a
better way to copy various representations of a key name/value/path
+ {
+ Variant variant = new Variant.string (model.get_suggested_key_copy_text (current_path,
browser_view.last_context_id));
+ menu.append (_("Copy descriptor"), "app.copy(" + variant.print (false) + ")");
+ }
+ else if (current_type != ViewType.SEARCH) */
+
+ populate_menu (ref menu);
+
+ append_app_actions_section (ref menu);
+
+ menu.freeze ();
+ info_button.set_menu_model ((MenuModel) menu);
+ }
+
+ protected virtual void populate_menu (ref GLib.Menu menu) {}
+
+ private void append_app_actions_section (ref GLib.Menu menu) // FIXME mnemonics?
+ {
+ GLib.Menu section = new GLib.Menu ();
+ append_or_not_night_mode_entry (ref section);
+ append_or_not_keyboard_shortcuts_entry (has_keyboard_shortcuts, !has_a_phone_size, ref section);
+ append_or_not_help_entry (has_help, ref section);
+ append_about_entry (about_action_label, ref section);
+ section.freeze ();
+ menu.append_section (null, section);
+ }
+
+ private static inline void append_or_not_keyboard_shortcuts_entry (bool has_keyboard_shortcuts,
+ bool show_keyboard_shortcuts,
+ ref GLib.Menu section)
+ {
+ // TODO something in small windows
+ if (!has_keyboard_shortcuts || !show_keyboard_shortcuts)
+ return;
+
+ /* Translators: usual menu entry of the hamburger menu (with a mnemonic that appears pressing Alt) */
+ section.append (_("_Keyboard Shortcuts"), "win.show-help-overlay");
+ }
+
+ private static inline void append_or_not_help_entry (bool has_help, ref GLib.Menu section)
+ {
+ if (!has_help)
+ return;
+
+ /* Translators: usual menu entry of the hamburger menu (with a mnemonic that appears pressing Alt) */
+ section.append (_("_Help"), "app.help");
+ }
+
+ private static inline void append_about_entry (string about_action_label, ref GLib.Menu section)
+ {
+ section.append (about_action_label, "base.about");
+ }
+
+ protected inline void hide_hamburger_menu ()
+ {
+ if (info_button.active)
+ info_button.active = false;
+ }
+
+ internal void toggle_hamburger_menu ()
+ {
+ if (info_button.visible)
+ info_button.active = !info_button.active;
+ else
+ toggle_view_menu ();
+ }
+ protected virtual void toggle_view_menu () {}
+
+ /*\
+ * * modes
+ \*/
+
+ protected signal void change_mode (uint8 mode_id);
+
+ private uint8 last_mode_id = 0; // 0 is default mode
+ protected uint8 register_new_mode ()
+ {
+ return ++last_mode_id;
+ }
+
+ protected static bool is_not_requested_mode (uint8 mode_id, uint8 requested_mode_id, ref bool
mode_is_active)
+ {
+ if (mode_id == requested_mode_id)
+ {
+ if (mode_is_active)
+ assert_not_reached ();
+ mode_is_active = true;
+ return false;
+ }
+ else
+ {
+ mode_is_active = false;
+ return true;
+ }
+ }
+
+ private uint8 current_mode_id = default_mode_id;
+ private void register_modes ()
+ {
+ register_default_mode ();
+ register_about_mode ();
+
+ this.change_mode.connect (update_current_mode_id);
+ }
+ private static void update_current_mode_id (BaseHeaderBar _this, uint8 requested_mode_id)
+ {
+ _this.current_mode_id = requested_mode_id;
+ }
+
+ /*\
+ * * default widgets
+ \*/
+
+ [GtkChild] private Button go_back_button;
+ [GtkChild] private Separator ltr_left_separator;
+ [GtkChild] private Label title_label;
+ [GtkChild] private MenuButton info_button;
+ [GtkChild] private Separator ltr_right_separator;
+
+ [GtkChild] protected Stack quit_button_stack;
+
+ protected void set_default_widgets_states (string? title_label_text_or_null,
+ bool show_go_back_button,
+ bool show_ltr_left_separator,
+ bool show_info_button,
+ bool show_ltr_right_separator,
+ bool show_quit_button_stack)
+ {
+ go_back_button.visible = show_go_back_button;
+ ltr_left_separator.visible = show_ltr_left_separator;
+ if (title_label_text_or_null == null)
+ {
+ title_label.set_label ("");
+ title_label.hide ();
+ }
+ else
+ {
+ title_label.set_label ((!) title_label_text_or_null);
+ title_label.show ();
+ }
+ info_button.visible = show_info_button;
+ ltr_right_separator.visible = show_ltr_right_separator;
+ quit_button_stack.visible = show_quit_button_stack;
+ }
+
+ /*\
+ * * default mode
+ \*/
+
+ protected const uint8 default_mode_id = 0;
+ private bool default_mode_on = true;
+ protected bool no_in_window_mode { get { return default_mode_on; }}
+
+ internal void show_default_view ()
+ {
+ change_mode (default_mode_id);
+ }
+
+ private void register_default_mode ()
+ {
+ this.change_mode.connect (mode_changed_default);
+ }
+
+ private static void mode_changed_default (BaseHeaderBar _this, uint8 requested_mode_id)
+ {
+ if (is_not_requested_mode (default_mode_id, requested_mode_id, ref _this.default_mode_on))
+ return;
+
+ _this.set_default_widgets_default_states (_this);
+ }
+
+ protected virtual void set_default_widgets_default_states (BaseHeaderBar _this)
+ {
+ _this.set_default_widgets_states (/* title_label text or null */ null,
+ /* show go_back_button */ false,
+ /* show ltr_left_separator */ false,
+ /* show info_button */ true,
+ /* show ltr_right_separator */ _this.disable_action_bar,
+ /* show quit_button_stack */ _this.disable_action_bar);
+ }
+
+ /*\
+ * * about mode
+ \*/
+
+ private uint8 about_mode_id = 0;
+ private bool about_mode_on = false;
+
+ internal void show_about_view ()
+ requires (about_mode_id > 0)
+ {
+ change_mode (about_mode_id);
+ }
+
+ private void register_about_mode ()
+ {
+ about_mode_id = register_new_mode ();
+
+ this.change_mode.connect (mode_changed_about);
+ }
+
+ private static void mode_changed_about (BaseHeaderBar _this, uint8 requested_mode_id)
+ requires (_this.about_mode_id > 0)
+ {
+ if (is_not_requested_mode (_this.about_mode_id, requested_mode_id, ref _this.about_mode_on))
+ return;
+
+ /* Translators: on really small windows, the about dialog is replaced by an in-window view; here is
the name of the view, displayed in the headerbar */
+ _this.set_default_widgets_states (_("About"), /* title_label text or null */
+ true, /* show go_back_button */
+ false, /* show ltr_left_separator */
+ false, /* show info_button */
+ false, /* show ltr_right_separator */
+ true); /* show quit_button_stack */
+ }
+
+ /*\
+ * * popovers methods
+ \*/
+
+ internal virtual void close_popovers ()
+ {
+ hide_hamburger_menu ();
+ }
+
+ internal virtual bool has_popover ()
+ {
+ return info_button.active;
+ }
+}
diff --git a/src/base-view.vala b/src/base-view.vala
new file mode 100644
index 0000000..4cd0066
--- /dev/null
+++ b/src/base-view.vala
@@ -0,0 +1,141 @@
+/*
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Taquin/ui/base-view.ui")]
+private class BaseView : Stack, AdaptativeWidget
+{
+ [GtkChild] protected Grid main_grid;
+
+ internal virtual bool handle_copy_text (out string copy_text)
+ {
+ if (in_window_about)
+ return about_list.handle_copy_text (out copy_text); // TODO copying logo...
+ else
+ return BaseWindow.no_copy_text (out copy_text);
+ }
+
+ internal virtual void close_popovers () {}
+
+ /*\
+ * * adaptative stuff
+ \*/
+
+ protected AdaptativeWidget.WindowSize saved_window_size { protected get; private set; default =
AdaptativeWidget.WindowSize.START_SIZE; }
+ protected virtual void set_window_size (AdaptativeWidget.WindowSize new_size)
+ {
+ saved_window_size = new_size;
+ if (about_list_created)
+ about_list.set_window_size (new_size);
+ if (notifications_revealer_created)
+ notifications_revealer.set_window_size (new_size);
+ }
+
+ /*\
+ * * in-window modes
+ \*/
+
+ internal virtual void show_default_view ()
+ {
+ if (in_window_about)
+ {
+ in_window_about = false;
+ set_visible_child (notifications_overlay); // or set_visible_child_name ("main-view");
+ }
+ else
+ assert_not_reached ();
+ }
+
+ internal virtual bool is_in_in_window_mode ()
+ {
+ return in_window_about;
+ }
+
+ /*\
+ * * in-window about
+ \*/
+
+ protected bool in_window_about { protected get; private set; default = false; }
+
+ private bool about_list_created = false;
+ private AboutList about_list;
+
+ internal void create_about_list (ref string [] artists,
+ ref string [] authors,
+ ref string comments,
+ ref string copyright,
+ ref string [] documenters,
+ ref string logo_icon_name,
+ ref string program_name,
+ ref string translator_credits,
+ ref string version,
+ ref string website,
+ ref string website_label)
+ {
+ about_list = new AboutList (/* needs shadows */ false,
+ /* big placeholder */ true,
+ ref artists, ref authors, ref comments, ref copyright, ref documenters,
ref logo_icon_name, ref program_name, ref translator_credits, ref version, ref website, ref website_label);
+ about_list.set_window_size (saved_window_size);
+ about_list.show ();
+ add (about_list);
+ about_list_created = true;
+ }
+
+ internal void show_about_view ()
+ requires (about_list_created == true)
+ requires (in_window_about == false)
+ {
+ about_list.reset ();
+ set_visible_child (about_list);
+ in_window_about = true;
+ }
+
+ /*\
+ * * notifications
+ \*/
+
+ [GtkChild] private Overlay notifications_overlay;
+
+ private bool notifications_revealer_created = false;
+ private NotificationsRevealer notifications_revealer;
+
+ private void create_notifications_revealer ()
+ {
+ notifications_revealer = new NotificationsRevealer ();
+ notifications_revealer.set_window_size (saved_window_size);
+ notifications_revealer.show ();
+ notifications_overlay.add_overlay (notifications_revealer);
+ notifications_revealer_created = true;
+ }
+
+ internal void show_notification (string notification)
+ {
+ if (!notifications_revealer_created)
+ create_notifications_revealer ();
+
+ notifications_revealer.show_notification (notification);
+ }
+
+ internal void hide_notification ()
+ {
+ if (!notifications_revealer_created)
+ return;
+
+ notifications_revealer.hide_notification ();
+ }
+}
diff --git a/src/base-window.vala b/src/base-window.vala
new file mode 100644
index 0000000..b061c79
--- /dev/null
+++ b/src/base-window.vala
@@ -0,0 +1,478 @@
+/*
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+private interface BaseApplication : Gtk.Application
+{
+ internal abstract void copy (string text);
+
+ // same as about dialog; skipped: wrap_license, license, license_type (forced at GPL v3+ ==
License.GPL_3_0), logo
+ internal abstract void get_about_dialog_infos (out string [] artists,
+ out string [] authors,
+ out string comments,
+ out string copyright,
+ out string [] documenters,
+ out string logo_icon_name,
+ out string program_name,
+ out string translator_credits,
+ out string version,
+ out string website,
+ out string website_label);
+}
+
+[GtkTemplate (ui = "/org/gnome/Taquin/ui/base-window.ui")]
+private class BaseWindow : AdaptativeWindow, AdaptativeWidget
+{
+ private BaseView main_view;
+ [CCode (notify = false)] public BaseView base_view
+ {
+ protected get { return main_view; }
+ protected construct
+ {
+ BaseView? _value = value;
+ if (_value == null)
+ assert_not_reached ();
+
+ main_view = value;
+ value.vexpand = true;
+ value.visible = true;
+ add_to_main_grid (value);
+ }
+ }
+
+ private BaseHeaderBar headerbar;
+
+ construct
+ {
+ headerbar = (BaseHeaderBar) nta_headerbar;
+
+ install_action_entries ();
+
+ add_adaptative_child (headerbar);
+ add_adaptative_child (main_view);
+ add_adaptative_child (this);
+ }
+
+ /*\
+ * * main grid
+ \*/
+
+ [GtkChild] private Grid main_grid;
+
+ protected void add_to_main_grid (Widget widget)
+ {
+ main_grid.add (widget);
+ }
+
+ /*\
+ * * action entries
+ \*/
+
+ private void install_action_entries ()
+ {
+ SimpleActionGroup action_group = new SimpleActionGroup ();
+ action_group.add_action_entries (action_entries, this);
+ insert_action_group ("base", action_group);
+ }
+
+ private const GLib.ActionEntry [] action_entries =
+ {
+ { "copy", copy }, // <P>c
+ { "copy-alt", copy_alt }, // <P>C
+
+ { "paste", paste }, // <P>v
+ { "paste-alt", paste_alt }, // <P>V
+
+ { "escape", _escape_pressed }, // Escape
+ { "toggle-hamburger", toggle_hamburger }, // F10
+ { "menu", menu_pressed }, // Menu
+
+ { "show-default-view", show_default_view },
+ { "about", about }
+ };
+
+ /*\
+ * * keyboard copy actions
+ \*/
+
+ protected virtual bool handle_copy_text (out string copy_text)
+ {
+ return main_view.handle_copy_text (out copy_text);
+ }
+ protected virtual bool get_alt_copy_text (out string copy_text)
+ {
+ return no_copy_text (out copy_text);
+ }
+ internal static bool no_copy_text (out string copy_text)
+ {
+ copy_text = "";
+ return false;
+ }
+ internal static bool copy_clipboard_text (out string copy_text)
+ {
+ string? nullable_selection = Clipboard.@get (Gdk.SELECTION_PRIMARY).wait_for_text ();
+ if (nullable_selection != null)
+ {
+ string selection = ((!) nullable_selection).dup ();
+ if (selection != "")
+ {
+ copy_text = selection;
+ return true;
+ }
+ }
+ return no_copy_text (out copy_text);
+ }
+ internal static inline bool is_empty_text (string text)
+ {
+ return text == "";
+ }
+
+ private void copy (/* SimpleAction action, Variant? path_variant */)
+ {
+ Widget? focus = get_focus ();
+ if (focus != null)
+ {
+ if ((!) focus is Editable) // GtkEntry, GtkSearchEntry, GtkSpinButton
+ {
+ int garbage1, garbage2;
+ if (((Editable) (!) focus).get_selection_bounds (out garbage1, out garbage2))
+ {
+ ((Editable) (!) focus).copy_clipboard ();
+ return;
+ }
+ }
+ else if ((!) focus is TextView)
+ {
+ if (((TextView) (!) focus).get_buffer ().get_has_selection ())
+ {
+ ((TextView) (!) focus).copy_clipboard ();
+ return;
+ }
+ }
+ else if ((!) focus is Label)
+ {
+ int garbage1, garbage2;
+ if (((Label) (!) focus).get_selection_bounds (out garbage1, out garbage2))
+ {
+ ((Label) (!) focus).copy_clipboard ();
+ return;
+ }
+ }
+ }
+
+ main_view.close_popovers ();
+
+ string text;
+ if (handle_copy_text (out text))
+ copy_text (text);
+ }
+
+ private void copy_alt (/* SimpleAction action, Variant? path_variant */)
+ {
+ if (main_view.is_in_in_window_mode ()) // TODO better
+ return;
+
+ main_view.close_popovers ();
+
+ string text;
+ if (get_alt_copy_text (out text))
+ copy_text (text);
+ }
+
+ private inline void copy_text (string text)
+ requires (!is_empty_text (text))
+ {
+ ((BaseApplication) get_application ()).copy ((!) text);
+ }
+
+ /*\
+ * * keyboard paste actions
+ \*/
+
+ protected virtual void paste_text (string? text) {}
+
+ private void paste (/* SimpleAction action, Variant? variant */)
+ {
+ if (main_view.is_in_in_window_mode ())
+ return;
+
+ Widget? focus = get_focus ();
+ if (focus != null)
+ {
+ if ((!) focus is Entry)
+ {
+ ((Entry) (!) focus).paste_clipboard ();
+ return;
+ }
+ if ((!) focus is TextView)
+ {
+ ((TextView) (!) focus).paste_clipboard ();
+ return;
+ }
+ }
+
+ paste_clipboard_content ();
+ }
+
+ private void paste_alt (/* SimpleAction action, Variant? variant */)
+ {
+ close_in_window_panels ();
+
+ paste_clipboard_content ();
+ }
+
+ private void paste_clipboard_content ()
+ {
+ Gdk.Display? display = Gdk.Display.get_default ();
+ if (display == null) // ?
+ return;
+
+ string? clipboard_content;
+ if (get_clipboard_content (out clipboard_content))
+ paste_text (clipboard_content);
+ }
+
+ private static inline bool get_clipboard_content (out string? clipboard_content)
+ {
+ Gdk.Display? display = Gdk.Display.get_default ();
+ if (display == null) // ?
+ {
+ clipboard_content = null; // garbage
+ return false;
+ }
+
+ clipboard_content = Clipboard.get_default ((!) display).wait_for_text ();
+ return true;
+ }
+
+ /*\
+ * * keyboard open menus actions
+ \*/
+
+ private void toggle_hamburger (/* SimpleAction action, Variant? variant */)
+ {
+ headerbar.toggle_hamburger_menu ();
+ main_view.close_popovers ();
+ }
+
+ protected virtual void menu_pressed (/* SimpleAction action, Variant? variant */)
+ {
+ headerbar.toggle_hamburger_menu ();
+ main_view.close_popovers ();
+ }
+
+ /*\
+ * * global callbacks
+ \*/
+
+ [GtkCallback]
+ protected virtual bool on_key_press_event (Widget widget, Gdk.EventKey event)
+ {
+ return _on_key_press_event (widget, event);
+ }
+ private static bool _on_key_press_event (Widget widget, Gdk.EventKey event)
+ {
+ string name = (!) (Gdk.keyval_name (event.keyval) ?? "");
+
+ if (name == "F1") // TODO fix dance done with the F1 & <Primary>F1 shortcuts that show help overlay
+ {
+ BaseWindow _this = (BaseWindow) widget;
+
+ _this.headerbar.close_popovers ();
+ _this.main_view.close_popovers ();
+ if ((event.state & Gdk.ModifierType.SHIFT_MASK) == 0)
+ return false; // help overlay
+ _this.about ();
+ return true;
+ }
+
+ return false;
+ }
+
+ /*\
+ * * adaptative stuff
+ \*/
+
+ private bool disable_popovers = false;
+ protected virtual void set_window_size (AdaptativeWidget.WindowSize new_size)
+ {
+ bool _disable_popovers = AdaptativeWidget.WindowSize.is_phone_size (new_size)
+ || AdaptativeWidget.WindowSize.is_extra_thin (new_size);
+ if (disable_popovers != _disable_popovers)
+ {
+ disable_popovers = _disable_popovers;
+ if (in_window_about)
+ show_default_view ();
+ }
+ }
+
+ /*\
+ * * in-window panels
+ \*/
+
+ protected virtual void close_in_window_panels ()
+ {
+ hide_notification ();
+ headerbar.close_popovers ();
+ if (in_window_about)
+ show_default_view ();
+ }
+
+ private void _escape_pressed (/* SimpleAction action, Variant? path_variant */)
+ {
+ escape_pressed (); // returns true if something is done
+ }
+ protected virtual bool escape_pressed ()
+ {
+ if (in_window_about)
+ {
+ show_default_view ();
+ return true;
+ }
+ return false;
+ }
+
+ protected virtual void show_default_view (/* SimpleAction action, Variant? path_variant */)
+ {
+ if (in_window_about)
+ {
+ in_window_about = false;
+ headerbar.show_default_view ();
+ main_view.show_default_view ();
+ }
+ else
+ assert_not_reached ();
+ }
+
+ /*\
+ * * about dialog
+ \*/
+
+ private AboutDialog about_dialog;
+ private void show_about_dialog ()
+ {
+ if (should_init_about_dialog)
+ {
+ create_about_dialog ();
+ about_dialog.response.connect ((_about_dialog, response) => _about_dialog.hide ());
+ about_dialog.key_press_event.connect (about_dialog_key_press_event);
+ about_dialog.set_transient_for (this);
+ should_init_about_dialog = false;
+ }
+ about_dialog.run ();
+ }
+ private static bool about_dialog_key_press_event (Widget _about_dialog_widget, Gdk.EventKey event)
+ {
+ if (((!) (Gdk.keyval_name (event.keyval) ?? "") == "F1")
+ && ((event.state & Gdk.ModifierType.SHIFT_MASK) != 0))
+ {
+ ((Dialog) _about_dialog_widget).response (ResponseType.CANCEL);
+ return true;
+ }
+ return false;
+ }
+
+ private bool should_init_about_dialog = true;
+ private void create_about_dialog ()
+ {
+ string [] artists, authors, documenters;
+ string comments, copyright, logo_icon_name, program_name, translator_credits, version, website,
website_label;
+
+ ((BaseApplication) get_application ()).get_about_dialog_infos (out artists, out authors, out
comments, out copyright, out documenters, out logo_icon_name, out program_name, out translator_credits, out
version, out website, out website_label);
+
+ about_dialog = new AboutDialog ();
+ about_dialog.set_title (headerbar.about_action_label);
+ about_dialog.set_wrap_license (true);
+ about_dialog.set_license_type (License.GPL_3_0); // forced, 1/3
+ if (artists.length > 0) about_dialog.set_artists (artists);
+ if (authors.length > 0) about_dialog.set_authors (authors);
+ if (comments != "") about_dialog.set_comments (comments);
+ if (copyright != "") about_dialog.set_copyright (copyright);
+ if (documenters.length > 0) about_dialog.set_documenters (documenters);
+ if (logo_icon_name != "") about_dialog.set_logo_icon_name (logo_icon_name);
+ if (program_name != "") about_dialog.set_program_name (program_name); else
assert_not_reached ();
+ if (translator_credits != "") about_dialog.set_translator_credits (translator_credits);
+ if (version != "") about_dialog.set_version (version);
+ if (website != "") about_dialog.set_website (website);
+ if (website_label != "") about_dialog.set_website_label (website_label);
+ }
+
+ /*\
+ * * in-window about
+ \*/
+
+ private void about (/* SimpleAction action, Variant? path_variant */)
+ {
+ if (disable_popovers)
+ toggle_in_window_about ();
+ else
+ show_about_dialog ();
+ }
+
+ [CCode (notify = false)] protected bool in_window_about { protected get; private set; default = false; }
+
+ private void toggle_in_window_about ()
+ {
+ if (should_init_in_window_about)
+ {
+ init_in_window_about ();
+ should_init_in_window_about = false;
+ show_about_view ();
+ }
+ else if (in_window_about)
+ show_default_view ();
+ else
+ show_about_view ();
+ }
+
+ private bool should_init_in_window_about = true;
+ private void init_in_window_about ()
+ {
+ string [] artists, authors, documenters;
+ string comments, copyright, logo_icon_name, program_name, translator_credits, version, website,
website_label;
+
+ ((BaseApplication) get_application ()).get_about_dialog_infos (out artists, out authors, out
comments, out copyright, out documenters, out logo_icon_name, out program_name, out translator_credits, out
version, out website, out website_label);
+
+ main_view.create_about_list (ref artists, ref authors, ref
comments, ref copyright, ref documenters, ref logo_icon_name, ref program_name, ref translator_credits, ref
version, ref website, ref website_label);
+ }
+
+ private inline void show_about_view ()
+ requires (in_window_about == false)
+ {
+ close_in_window_panels ();
+
+ in_window_about = true;
+ headerbar.show_about_view ();
+ main_view.show_about_view ();
+ set_focus_visible (false); // about-list grabs focus
+ }
+
+ /*\
+ * * notifications
+ \*/
+
+ protected void show_notification (string notification)
+ {
+ main_view.show_notification (notification);
+ }
+
+ protected void hide_notification ()
+ {
+ main_view.hide_notification ();
+ }
+}
diff --git a/src/meson.build b/src/meson.build
index 53e83b4..247839e 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -2,9 +2,19 @@ resources = gnome.compile_resources (meson.project_name(), 'taquin.gresource.xml
executable(meson.project_name(),[
'config.vapi',
+
+ 'about-list.vala',
+ 'adaptative-window.vala',
+ 'base-headerbar.vala',
+ 'base-view.vala',
+ 'base-window.vala',
'game-headerbar.vala',
'game-view.vala',
'game-window.vala',
+ 'night-light-monitor.vala',
+ 'notifications-revealer.vala',
+ 'overlayed-list.vala',
+ 'registry-placeholder.vala',
'taquin-game.vala',
'taquin-main.vala',
'taquin-view.vala'
diff --git a/src/night-light-monitor.vala b/src/night-light-monitor.vala
new file mode 100644
index 0000000..ea99fa8
--- /dev/null
+++ b/src/night-light-monitor.vala
@@ -0,0 +1,222 @@
+/*
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+*/
+/*
+ This code has been inspired by the one for a similar function in Calendar
+ https://gitlab.gnome.org/GNOME/gnome-calendar commit 474ce8b9da4a3322f2fe
+ Copyright 2018 Georges Basile Stavracas Neto <georges stavracas gmail com>
+*/
+
+private class NightLightMonitor : Object
+{
+ // warnings callable during init
+ private const string warning_init_connection =
+ "Impossible to get connection for session bus, night-light mode disabled.";
+ private const string warning_init_dbus_proxy =
+ "Impossible to get dbus proxy for session bus, night-light mode disabled.";
+ private const string warning_cached_property =
+ "Impossible to get cached property from proxy, night-light mode disabled.";
+
+ // warnings callable when setting mode
+ private const string warning_check_your_logs =
+ "Something went wrong during the night mode support init, doing nothing.";
+ private const string warning_get_gtksettings =
+ "Something went wrong getting GtkSettings default object, doing nothing.";
+
+ // schema static things
+ private const string schema_name = "org.gnome.Taquin.NightLight"; // TODO put in a library
+ private const string automatic_night_mode_key = "automatic-night-mode";
+
+ /*\
+ * * Public interface
+ \*/
+
+ [CCode (notify = false)] public string schema_path { private get; internal construct; }
+
+ public enum NightTime {
+ UNKNOWN,
+ NIGHT,
+ DAY;
+
+ internal static bool should_use_dark_theme (NightTime state)
+ {
+ return state == NightTime.NIGHT;
+ }
+ }
+ [CCode (notify = true)] public NightTime night_time { internal get; private construct set;
default = NightTime.UNKNOWN; }
+ [CCode (notify = true)] public bool dark_theme { internal get; private construct set;
default = false; }
+ [CCode (notify = true)] public bool automatic_night_mode { internal get; private construct set;
default = false; }
+
+ internal NightLightMonitor (string _schema_path)
+ {
+ Object (schema_path: _schema_path);
+ if (night_time == NightTime.UNKNOWN) // disables mode if night_time has not been set at first try
// TODO specific warning?
+ return;
+ connect_properties ();
+ }
+
+ internal void set_use_night_mode (bool night_mode_state)
+ {
+ if (night_time == NightTime.UNKNOWN) // do nothing but warn
+ warning (warning_check_your_logs);
+ else
+ {
+ paused = !night_mode_state;
+ update ();
+ }
+ }
+
+ /*\
+ * * Init proxy
+ \*/
+
+ private GLib.Settings settings;
+ private DBusProxy proxy;
+
+ construct
+ {
+ settings = new GLib.Settings.with_path (schema_name, schema_path);
+ automatic_night_mode = settings.get_boolean (automatic_night_mode_key);
+ paused = !automatic_night_mode;
+
+ DBusConnection? connection;
+ init_connection (out connection);
+ if (connection == null)
+ return;
+
+ DBusProxy? nullable_proxy;
+ init_proxy ((!) connection, out nullable_proxy);
+ if (nullable_proxy == null)
+ return;
+
+ proxy = (!) nullable_proxy;
+ night_time = get_updated_night_time (ref proxy, automatic_night_mode);
+ }
+
+ private static void init_connection (out DBusConnection? connection)
+ {
+ try
+ {
+ connection = Bus.get_sync (BusType.SESSION, null);
+ }
+ catch (Error e)
+ {
+ warning (warning_init_connection);
+ warning (e.message);
+ connection = null;
+ }
+ }
+
+ private static void init_proxy (DBusConnection connection, out DBusProxy? proxy)
+ {
+ try
+ {
+ proxy = new DBusProxy.sync (connection,
+ DBusProxyFlags.GET_INVALIDATED_PROPERTIES,
+ null,
+ "org.gnome.SettingsDaemon.Color",
+ "/org/gnome/SettingsDaemon/Color",
+ "org.gnome.SettingsDaemon.Color",
+ null);
+ }
+ catch (Error e)
+ {
+ warning (warning_init_dbus_proxy);
+ warning (e.message);
+ proxy = null;
+ }
+ }
+
+ private static NightTime get_updated_night_time (ref DBusProxy proxy, bool automatic_night_mode)
+ {
+ Variant? variant_active = proxy.get_cached_property ("NightLightActive");
+ Variant? variant_paused = proxy.get_cached_property ("DisabledUntilTomorrow");
+ if (variant_active == null)
+ {
+ warning (warning_cached_property);
+ return NightTime.UNKNOWN;
+ }
+
+ bool night_time_is_night = ((!) variant_active).get_boolean ();
+ if (variant_paused != null)
+ night_time_is_night = night_time_is_night && !(((!) variant_paused).get_boolean () &&
automatic_night_mode);
+
+ if (night_time_is_night)
+ return NightTime.NIGHT;
+ else
+ return NightTime.DAY;
+ }
+
+ /*\
+ * * Private methods
+ \*/
+
+ private bool paused = false;
+
+ private void connect_properties ()
+ {
+ proxy.g_properties_changed.connect (() => {
+ night_time = get_updated_night_time (ref proxy, automatic_night_mode);
+ if (night_time != NightTime.NIGHT)
+ paused = !automatic_night_mode;
+ update ();
+ });
+ settings.changed [automatic_night_mode_key].connect ((_settings, _key_name) => {
+ automatic_night_mode = _settings.get_boolean (_key_name);
+ paused = !automatic_night_mode;
+ update ();
+ });
+ update ();
+ }
+
+ private void update ()
+ {
+ if (automatic_night_mode)
+ {
+ if (paused)
+ set_dark_theme_real (false);
+ else
+ set_dark_theme_real (NightTime.should_use_dark_theme (night_time));
+ }
+ else if (night_time == NightTime.NIGHT)
+ {
+ if (paused)
+ set_dark_theme_real (false);
+ else
+ set_dark_theme_real (true);
+ }
+ else
+ set_dark_theme_real (false);
+ }
+ private void set_dark_theme_real (bool night_mode)
+ {
+ if (_set_dark_theme_real (night_mode))
+ dark_theme = night_mode;
+ }
+ private static bool _set_dark_theme_real (bool night_mode)
+ {
+ Gtk.Settings? gtk_settings = Gtk.Settings.get_default ();
+ if (gtk_settings == null)
+ {
+ warning (warning_get_gtksettings);
+ return false;
+ }
+
+ if (night_mode != ((!) gtk_settings).gtk_application_prefer_dark_theme)
+ ((!) gtk_settings).@set ("gtk-application-prefer-dark-theme", night_mode);
+ return true;
+ }
+}
diff --git a/src/notifications-revealer.vala b/src/notifications-revealer.vala
new file mode 100644
index 0000000..8831e3e
--- /dev/null
+++ b/src/notifications-revealer.vala
@@ -0,0 +1,80 @@
+/*
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Taquin/ui/notifications-revealer.ui")]
+private class NotificationsRevealer : Revealer, AdaptativeWidget
+{
+ [GtkChild] private Label notification_label;
+
+ construct
+ {
+ install_action_entries ();
+ }
+
+ /*\
+ * * internal calls
+ \*/
+
+ internal void show_notification (string notification)
+ {
+ notification_label.set_text (notification);
+ set_reveal_child (true);
+ }
+
+ private bool is_thin = false;
+ private void set_window_size (AdaptativeWidget.WindowSize new_size)
+ {
+ bool _is_thin = AdaptativeWidget.WindowSize.is_quite_thin (new_size);
+ if (is_thin == _is_thin)
+ return;
+ is_thin = _is_thin;
+
+ if (_is_thin)
+ {
+ hexpand = true;
+ halign = Align.FILL;
+ }
+ else
+ {
+ hexpand = false;
+ halign = Align.CENTER;
+ }
+ }
+
+ /*\
+ * * action entries
+ \*/
+
+ private void install_action_entries ()
+ {
+ SimpleActionGroup action_group = new SimpleActionGroup ();
+ action_group.add_action_entries (action_entries, this);
+ insert_action_group ("notification", action_group);
+ }
+
+ private const GLib.ActionEntry [] action_entries =
+ {
+ { "hide", hide_notification }
+ };
+
+ internal void hide_notification (/* SimpleAction action, Variant? variant */)
+ {
+ set_reveal_child (false);
+ }
+}
diff --git a/src/overlayed-list.vala b/src/overlayed-list.vala
new file mode 100644
index 0000000..3fbb1c3
--- /dev/null
+++ b/src/overlayed-list.vala
@@ -0,0 +1,385 @@
+/*
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Taquin/ui/overlayed-list.ui")]
+private abstract class OverlayedList : Overlay, AdaptativeWidget
+{
+ [GtkChild] protected ListBox main_list_box;
+ private StyleContext main_list_box_context;
+ protected GLib.ListStore main_list_store = new GLib.ListStore (typeof (Widget));
+
+ [GtkChild] private ScrolledWindow scrolled;
+ [GtkChild] private Box edit_mode_box;
+
+ /*\
+ * * differed construct
+ \*/
+
+ construct
+ {
+ main_list_box_context = main_list_box.get_style_context ();
+ main_context = get_style_context ();
+ connect_handlers ();
+ main_list_box.bind_model (main_list_store, create_rows);
+ }
+
+ private Widget create_rows (Object item)
+ {
+ return (Widget) item;
+ }
+
+
+ [GtkChild] private ModelButton enter_edit_mode_button;
+ [GtkChild] private ModelButton leave_edit_mode_button;
+ [CCode (notify = false)] public string edit_mode_action_prefix
+ {
+ construct
+ {
+ get_style_context ().add_class ("needs-padding");
+
+ // TODO sanitize "value"
+ enter_edit_mode_button.set_detailed_action_name (value + ".set-edit-mode(true)");
+ leave_edit_mode_button.set_detailed_action_name (value + ".set-edit-mode(false)");
+ }
+ }
+ [CCode (notify = false)] public string first_mode_name { protected set { leave_edit_mode_button.text =
value; }}
+ [CCode (notify = false)] public string second_mode_name { protected set { enter_edit_mode_button.text =
value; }}
+
+ [CCode (notify = false)] public bool needs_shadows
+ {
+ construct
+ {
+ if (value)
+ scrolled.shadow_type = ShadowType.ETCHED_IN;
+ else
+ scrolled.shadow_type = ShadowType.NONE;
+ }
+ }
+
+ protected string placeholder_icon;
+ protected string placeholder_text;
+ [CCode (notify = false)] public bool big_placeholder { private get; internal construct; }
+ protected void add_placeholder ()
+ {
+ RegistryPlaceholder placeholder = new RegistryPlaceholder (placeholder_icon, placeholder_text,
big_placeholder);
+ main_list_box.set_placeholder (placeholder);
+ }
+
+ /*\
+ * * responsive design
+ \*/
+
+ private StyleContext main_context;
+ internal void set_window_size (AdaptativeWidget.WindowSize new_size)
+ {
+ if (!AdaptativeWidget.WindowSize.is_extra_thin (new_size) &&
AdaptativeWidget.WindowSize.is_extra_flat (new_size))
+ set_horizontal (ref main_context, ref edit_mode_box);
+ else
+ set_vertical (ref main_context, ref edit_mode_box);
+ }
+ private static inline void set_horizontal (ref StyleContext main_context, ref Box edit_mode_box)
+ {
+ main_context.remove_class ("vertical");
+ edit_mode_box.halign = Align.END;
+ edit_mode_box.valign = Align.CENTER;
+ edit_mode_box.orientation = Orientation.VERTICAL;
+ edit_mode_box.width_request = 160;
+ main_context.add_class ("horizontal");
+ }
+ private static inline void set_vertical (ref StyleContext main_context, ref Box edit_mode_box)
+ {
+ main_context.remove_class ("horizontal");
+ edit_mode_box.halign = Align.CENTER;
+ edit_mode_box.valign = Align.END;
+ edit_mode_box.orientation = Orientation.HORIZONTAL;
+ edit_mode_box.width_request = 200;
+ main_context.add_class ("vertical");
+ }
+
+ /*\
+ * * keyboard
+ \*/
+
+ internal bool next_match ()
+ {
+ return _next_match (ref main_list_box);
+ }
+ private static inline bool _next_match (ref ListBox main_list_box)
+ {
+ ListBoxRow? row = main_list_box.get_selected_row (); // TODO multiple rows and focus-only lists
+ if (row == null)
+ row = main_list_box.get_row_at_index (0);
+ else
+ row = main_list_box.get_row_at_index (((!) row).get_index () + 1);
+
+ if (row == null)
+ {
+ _scroll_bottom (ref main_list_box);
+ return false;
+ }
+ main_list_box.select_row ((!) row);
+ ((!) row).grab_focus ();
+ return true;
+ }
+
+ internal bool previous_match ()
+ {
+ return _previous_match (ref main_list_box);
+ }
+ private static inline bool _previous_match (ref ListBox main_list_box)
+ {
+ uint n_items = main_list_box.get_children ().length (); // FIXME OverlayedList.n_items is unreliable
+ if (n_items == 0)
+ return false;
+
+ ListBoxRow? row = main_list_box.get_selected_row (); // TODO multiple rows and focus-only lists
+ if (row == null)
+ row = main_list_box.get_row_at_index ((int) n_items - 1);
+ else
+ {
+ int index = ((!) row).get_index ();
+ if (index <= 0)
+ return false;
+ row = main_list_box.get_row_at_index (index - 1);
+ }
+
+ if (row == null)
+ assert_not_reached ();
+
+ main_list_box.select_row ((!) row);
+ ((!) row).grab_focus ();
+ return true;
+ }
+
+ internal void select_all ()
+ {
+ main_list_box.select_all ();
+ }
+
+ internal void unselect_all ()
+ {
+ main_list_box.unselect_all ();
+ }
+
+ protected void select_row_for_real (ListBoxRow row) // ahem...
+ {
+ main_list_box.unselect_row (row);
+ main_list_box.select_row (row);
+ }
+
+ /*\
+ * * utilities
+ \*/
+
+ protected int [] get_selected_rows_indices ()
+ {
+ return _get_selected_rows_indices (ref main_list_box);
+ }
+ private static inline int [] _get_selected_rows_indices (ref ListBox main_list_box)
+ {
+ int [] indices = new int [0];
+ main_list_box.selected_foreach ((_list_box, selected_row) => {
+ int index = selected_row.get_index ();
+ if (index < 0)
+ assert_not_reached ();
+ indices += index;
+ });
+ return indices;
+ }
+
+ protected void scroll_top ()
+ {
+ _scroll_top (ref main_list_box);
+ }
+ private static inline void _scroll_top (ref ListBox main_list_box)
+ {
+ Adjustment adjustment = main_list_box.get_adjustment ();
+ adjustment.set_value (adjustment.get_lower ());
+ }
+
+ protected void scroll_bottom ()
+ {
+ _scroll_bottom (ref main_list_box);
+ }
+ private static inline void _scroll_bottom (ref ListBox main_list_box)
+ {
+ Adjustment adjustment = main_list_box.get_adjustment ();
+ adjustment.set_value (adjustment.get_upper ());
+ }
+
+ internal bool handle_copy_text (out string copy_text)
+ {
+ return _handle_copy_text (out copy_text, ref main_list_box);
+ }
+ private static inline bool _handle_copy_text (out string copy_text, ref ListBox main_list_box)
+ {
+ List<weak ListBoxRow> selected_rows = main_list_box.get_selected_rows ();
+ OverlayedListRow row;
+ switch (selected_rows.length ())
+ {
+ case 0:
+ Widget? focus_child = main_list_box.get_focus_child ();
+ if (focus_child == null)
+ return BaseWindow.copy_clipboard_text (out copy_text);
+ if (BaseWindow.copy_clipboard_text (out copy_text))
+ return true;
+ if (!((!) focus_child is OverlayedListRow))
+ assert_not_reached ();
+ row = (OverlayedListRow) (!) focus_child;
+ break;
+ case 1:
+ ListBoxRow selected_row = selected_rows.nth_data (0);
+ if (!(selected_row is OverlayedListRow))
+ assert_not_reached ();
+ row = (OverlayedListRow) selected_row;
+ break;
+ default:
+ return BaseWindow.no_copy_text (out copy_text);
+ }
+ return row.handle_copy_text (out copy_text); // FIXME row should keep focus
+ }
+
+ /*\
+ * * selection state
+ \*/
+
+ internal signal void selection_changed ();
+
+ [GtkCallback]
+ private void on_selection_changed ()
+ {
+ selection_changed ();
+ }
+
+ internal enum SelectionState {
+ EMPTY,
+ // one
+ UNIQUE,
+ FIRST,
+ LAST,
+ MIDDLE,
+ // multiple
+ MULTIPLE,
+ MULTIPLE_FIRST,
+ MULTIPLE_LAST,
+ ALL
+ }
+
+ internal SelectionState get_selection_state ()
+ {
+ return _get_selection_state (ref main_list_box, ref main_list_store);
+ }
+ private static inline SelectionState _get_selection_state (ref ListBox main_list_box, ref GLib.ListStore
main_list_store)
+ {
+ List<weak ListBoxRow> selected_rows = main_list_box.get_selected_rows ();
+ uint n_selected_rows = selected_rows.length ();
+
+ if (n_selected_rows == 0)
+ return SelectionState.EMPTY;
+ if (n_selected_rows >= 2)
+ {
+ uint n_items = main_list_store.get_n_items ();
+ if (n_selected_rows == n_items)
+ return SelectionState.ALL;
+ uint first_items = 0;
+ uint last_items = 0;
+ uint first_of_the_last_items_index = n_items - n_selected_rows;
+ selected_rows.foreach ((row) => {
+ uint index = row.get_index ();
+ if (index < n_selected_rows)
+ first_items++;
+ if (index >= first_of_the_last_items_index)
+ last_items++;
+ });
+ if (first_items == n_selected_rows)
+ return SelectionState.MULTIPLE_FIRST;
+ if (last_items == n_selected_rows)
+ return SelectionState.MULTIPLE_LAST;
+ return SelectionState.MULTIPLE;
+ }
+
+ int index = selected_rows.nth_data (0).get_index ();
+ bool is_first = index == 0;
+ bool is_last = main_list_box.get_row_at_index (index + 1) == null;
+ if (is_first && is_last)
+ return SelectionState.UNIQUE;
+ if (is_first)
+ return SelectionState.FIRST;
+ if (is_last)
+ return SelectionState.LAST;
+ return SelectionState.MIDDLE;
+ }
+
+ /*\
+ * * overlay visibility
+ \*/
+
+ protected ulong content_changed_handler = 0;
+
+ [CCode (notify = false)] protected uint n_items { protected get; private set; default = 0; }
+ private bool is_editable = false;
+
+ protected void change_editability (bool new_value)
+ {
+ is_editable = new_value;
+ update_edit_mode_box_visibility ();
+ }
+
+ private void connect_handlers () // connect and disconnect manually or bad things happen on destroy
+ {
+ content_changed_handler = main_list_store.items_changed.connect (on_content_changed);
+
+ destroy.connect (() => main_list_store.disconnect (content_changed_handler));
+ }
+
+ private void on_content_changed (GLib.ListModel main_list_model, uint position, uint removed, uint added)
+ {
+ n_items += added;
+ n_items -= removed;
+ update_has_empty_list_class (n_items == 0);
+ update_edit_mode_box_visibility ();
+ }
+
+ private bool has_empty_list_class = false;
+ private void update_has_empty_list_class (bool list_is_empty)
+ {
+ if (list_is_empty && !has_empty_list_class)
+ {
+ main_list_box_context.add_class ("empty-list");
+ has_empty_list_class = true;
+ }
+ else if (!list_is_empty && has_empty_list_class)
+ {
+ has_empty_list_class = false;
+ main_list_box_context.remove_class ("empty-list");
+ }
+ }
+
+ private inline void update_edit_mode_box_visibility ()
+ {
+ edit_mode_box.visible = is_editable && n_items != 0;
+ }
+
+ internal abstract void reset ();
+}
+
+private abstract class OverlayedListRow : ListBoxRow
+{
+ internal abstract bool handle_copy_text (out string copy_text);
+}
diff --git a/src/registry-placeholder.vala b/src/registry-placeholder.vala
new file mode 100644
index 0000000..b92c464
--- /dev/null
+++ b/src/registry-placeholder.vala
@@ -0,0 +1,54 @@
+/*
+ This file is part of GNOME Taquin
+
+ GNOME Taquin 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 3 of the License, or
+ (at your option) any later version.
+
+ GNOME Taquin is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME Taquin. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Taquin/ui/registry-placeholder.ui")]
+private class RegistryPlaceholder : Grid
+{
+ [GtkChild] private Label placeholder_label;
+ [GtkChild] private Image placeholder_image;
+
+ [CCode (notify = false)] public string label { internal construct set { placeholder_label.label =
value; }}
+ [CCode (notify = false)] public string icon_name { private get; internal construct; }
+ [CCode (notify = false)] public bool big
+ {
+ internal construct set
+ {
+ if (value)
+ {
+ placeholder_image.pixel_size = 72;
+ get_style_context ().add_class ("big-popover");
+ }
+ else
+ {
+ placeholder_image.pixel_size = 36;
+ get_style_context ().remove_class ("big-popover");
+ }
+ }
+ }
+
+ construct
+ {
+ placeholder_image.icon_name = icon_name;
+ }
+
+ internal RegistryPlaceholder (string _icon_name, string _label, bool _big)
+ {
+ Object (icon_name:_icon_name, label: _label, big: _big);
+ }
+}
diff --git a/src/taquin-main.vala b/src/taquin-main.vala
index dd1c316..84c34fe 100644
--- a/src/taquin-main.vala
+++ b/src/taquin-main.vala
@@ -388,4 +388,52 @@ public class Taquin : Gtk.Application
Canberra.PROP_MEDIA_NAME, name,
Canberra.PROP_MEDIA_FILENAME, Path.build_filename (SOUND_DIRECTORY,
"%s.ogg".printf (name)));
}
+
+ /*\
+ * * Copy action
+ \*/
+
+ internal void copy (string text)
+ {
+ Gdk.Display? display = Gdk.Display.get_default ();
+ if (display == null)
+ return;
+
+ Gtk.Clipboard clipboard = Gtk.Clipboard.get_default ((!) display);
+ clipboard.set_text (text, text.length);
+ }
+}
+
+namespace AboutDialogInfos
+{
+ // strings
+ internal const string program_name = Taquin.PROGRAM_NAME;
+ internal const string version = VERSION;
+
+ /* Translators: about dialog text */
+ internal const string comments = _("A classic 15-puzzle game");
+
+ /* Translators: about dialog text */
+ internal const string copyright = "Copyright \xc2\xa9 2014-2019 – Arnaud Bonatti"; // TODO translation;
autogen, to not change each year?
+
+ /* Translators: about dialog text; this string should be replaced by a text crediting yourselves and
your translation team, or should be left empty. Do not translate literally! */
+ internal const string translator_credits = _("translator-credits");
+
+ // various
+ internal const string logo_icon_name = "gnome-taquin";
+ internal const string website = "https://wiki.gnome.org/Apps/Taquin";
+ /* Translators: about dialog text; label of the website link */
+ internal const string website_label = _("Page on GNOME wiki");
+ internal const string [] authors = { "Arnaud Bonatti" };
+ internal const License license_type = License.GPL_3_0; /* means "version 3.0 or later" */
+ internal const string [] artists = {
+ "Abelard (Wikimedia)",
+ "Alvesgaspar (Wikimedia)",
+ "Mueller-rech.muenchen (Wikimedia)",
+ "Ruskis (Wikimedia)",
+ "Toyah (Wikimedia)"
+ /* Translators: about dialog text; in the Credits, text at the end of the "Artwork by" section */
+// _("(see COPYING.themes for informations)") // FIXME
+ };
+ internal const string [] documenters = { "Arnaud Bonatti" };
}
diff --git a/src/taquin.gresource.xml b/src/taquin.gresource.xml
index c8c96e1..aa68947 100644
--- a/src/taquin.gresource.xml
+++ b/src/taquin.gresource.xml
@@ -5,7 +5,15 @@
<file preprocess="to-pixdata" alias="16-Puzzle.png">../data/schema-16.png</file>
</gresource>
<gresource prefix="/org/gnome/Taquin/ui">
+ <file preprocess="xml-stripblanks" alias="adaptative-window.ui">../data/adaptative-window.ui</file>
+ <file preprocess="xml-stripblanks" alias="base-headerbar.ui">../data/base-headerbar.ui</file>
+ <file preprocess="xml-stripblanks" alias="base-view.ui">../data/base-view.ui</file>
+ <file alias="base-window.css">../data/base-window.css</file>
+ <file preprocess="xml-stripblanks" alias="base-window.ui">../data/base-window.ui</file>
<file preprocess="xml-stripblanks" alias="game-headerbar.ui">../data/game-headerbar.ui</file>
+ <file preprocess="xml-stripblanks"
alias="notifications-revealer.ui">../data/notifications-revealer.ui</file>
+ <file preprocess="xml-stripblanks" alias="overlayed-list.ui">../data/overlayed-list.ui</file>
+ <file preprocess="xml-stripblanks" alias="registry-placeholder.ui">../data/registry-placeholder.ui</file>
<file preprocess="xml-stripblanks" alias="taquin-screens.ui">../data/taquin-screens.ui</file>
<file alias="taquin.css">../data/taquin.css</file>
</gresource>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]