[gnome-taquin] Import code from Dconf Editor.



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]