Merge lp:~elementary-apps/granite/saved-state into lp:~elementary-pantheon/granite/granite

Proposed by Raphael Isemann
Status: Rejected
Rejected by: xapantu
Proposed branch: lp:~elementary-apps/granite/saved-state
Merge into: lp:~elementary-pantheon/granite/granite
Diff against target: 250 lines (+229/-1)
2 files modified
lib/CMakeLists.txt (+2/-1)
lib/Services/SavedState.vala (+227/-0)
To merge this branch: bzr merge lp:~elementary-apps/granite/saved-state
Reviewer Review Type Date Requested Status
xapantu (community) Disapprove
Rico Tzschichholz Needs Information
Review via email: mp+232092@code.launchpad.net

Description of the change

The current system of the saved-state logic is currently done by each app on their own, and the result is either half-implemented, wrong-implemented, not-implemented or twice-implemented processes for syncing the window-state to the gsettings-backend.

This branch adds a simple API extension to Granite that allows to sync a specified window to a single specified gsettings-key.

To test this branch i implemented a useage of this API in Terminal: https://code.launchpad.net/~elementary-apps/pantheon-terminal/new-saved-state/

To post a comment you must log in.
Revision history for this message
Rico Tzschichholz (ricotz) wrote :

This is what is suppose to be achieved with the following:

http://valadoc.org/#!api=gio-2.0/GLib.Settings.bind
http://valadoc.org/#!api=gio-2.0/GLib.Settings.bind_with_mapping
http://valadoc.org/#!api=gio-2.0/GLib.Settings.bind_writable

Besides that "SavedState" is not really descriptive enough since only Gtk.Windows are supported

review: Needs Information
Revision history for this message
Raphael Isemann (teemperor) wrote :

SavedState is not a good description. We want that it takes care of saving/restoring the window-geometry over long durations, so we could name it "PersistentWindowManager"? I'm open for suggestions. We probably also want to rename the key from "saved-state" to something like "saved-geometry".

I'm not sure what you mean with "This is what is suppose to be achieved with the following: ..."? That we should have one Variant that we bind to a GSettings-key?

Revision history for this message
David Gomes (davidgomes) wrote :

Wouldn't it be better to have our own Granite.Window, instead of a wrapper class like you made? I mean, if it is doable, of course.

Revision history for this message
Raphael Isemann (teemperor) wrote :

Sounds reasonable and would solve the naming problem.

So it would be just using Granite.Window that has a property for a gsettings-path where it would save its geometry.

Revision history for this message
Akshay Shekher (voldyman) wrote :

hmm, my idea was of storing and restoring windows state from applications that use Granite.Application transparently.

Gtk.Application has signals for window_added and window_removed, we could use those signals to get the dimensions of the windows when they were closed and restore when the same window is opened again.

I haven't thought enough about this so it is open for debate.

Revision history for this message
xapantu (xapantu) wrote :

I also think this must be done more transparently. Maybe a set_main_window call or whatever would be required, but that should be all. Creating a new object and calling several functions for such a simple task is not very good. Besides, as Rico said, it would be better to bind the properties, else that will make a lot of code for nothing.

review: Disapprove

Unmerged revisions

783. By Raphael Isemann

Added support for maximized_fullscreen and fixed the saving of the window-state to the dconf-base (before we used the wrong method with the same name from Gtk.Widget instead of Gdk.Window). Also moved the methods to the upper part of the class and expanded documentation a bit.

782. By Raphael Isemann

Fixed saved-state parsing and fixed setting the window-size

781. By Raphael Isemann

We moved to a single key now as the backend

780. By Raphael Isemann

Added SavedState class

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/CMakeLists.txt'
2--- lib/CMakeLists.txt 2014-01-18 21:47:46 +0000
3+++ lib/CMakeLists.txt 2014-08-25 14:36:17 +0000
4@@ -12,6 +12,7 @@
5 Drawing/Utilities.vala
6 GtkPatch/AboutDialog.vala
7 Services/Settings.vala
8+ Services/SavedState.vala
9 Services/Logger.vala
10 Services/Paths.vala
11 Services/System.vala
12@@ -114,4 +115,4 @@
13 if (INTROSPECTION_FOUND)
14 include (GObjectIntrospectionMacros)
15 add_target_gir (${PKG_NAME} ${PKG_GIR_NAME} ${PKG_NAME}.h "${VALA_C}" "${DEPS_CFLAGS}" ${API_VERSION} ${GI_PKG_DEPS})
16-endif ()
17\ No newline at end of file
18+endif ()
19
20=== added file 'lib/Services/SavedState.vala'
21--- lib/Services/SavedState.vala 1970-01-01 00:00:00 +0000
22+++ lib/Services/SavedState.vala 2014-08-25 14:36:17 +0000
23@@ -0,0 +1,227 @@
24+/***
25+ Copyright (C) 2014 Raphael Isemann <raphael@elementaryos.org>
26+
27+ This program or library is free software; you can redistribute it
28+ and/or modify it under the terms of the GNU Lesser General Public
29+ License as published by the Free Software Foundation; either
30+ version 3 of the License, or (at your option) any later version.
31+
32+ This library is distributed in the hope that it will be useful,
33+ but WITHOUT ANY WARRANTY; without even the implied warranty of
34+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35+ Lesser General Public License for more details.
36+
37+ You should have received a copy of the GNU Lesser General
38+ Public License along with this library; if not, write to the
39+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
40+ Boston, MA 02110-1301 USA.
41+***/
42+
43+namespace Granite.Services {
44+
45+ /**
46+ + The SavedState class provides a synchronizing mechanism between
47+ * the properties of a Gtk.Window and a gsettings-schema for saving
48+ * those properties.
49+ *
50+ * The only action in your code is creating a SavedState-object, the rest is
51+ * handled by this class:
52+ * {{{
53+ * new SavedState (my_app_window, "org.example.myapp");
54+ * }}}
55+ *
56+ * Note that you should use this code between the show_all () and the end of
57+ * your UI-setup phase to prevent unwanted side-effects.
58+ *
59+ * You also need a proper saved-state key in your schema with the name "saved-state"
60+ * and with the type '(iiiiu)'
61+ * where the types represent in the order of listing:
62+ * x-coordinate of the window (the value -1 means centered window),
63+ * y-coordinate of the window (the value -1 means centered window),
64+ * width of the window,
65+ * height of the window,
66+ * state of the window (0 = Normal, 1 = Maximized, 2 = Fullscreen).
67+ *
68+ * This is an example-of such an gsettings-key in your myapp.gschema.xml:
69+ *
70+ * {{{
71+ * ...
72+ * <key name="saved-state" type="(iiiiu)">
73+ * <default>(-1,-1,700,500,0)</default>
74+ * <summary>The saved state of the window.</summary>
75+ * <description>The saved width of the window. The values represent in the respective order:
76+ * x-coordinate of the window (the value -1 means centered window),y-coordinate of the window (the value -1 means centered window),
77+ * width of the window, height of the window, state of the window (0 = Normal, 1 = Maximized, 2 = Fullscreen).</description>
78+ * </key>
79+ * ...
80+ * }}}
81+ *
82+ */
83+ public class SavedState {
84+
85+ SavedStateBackend backend;
86+ Gtk.Window window;
87+
88+ /**
89+ * Connects the given window to the saved-state in the given schema.
90+ * All window-contents should haven been be added when this constructor
91+ * is called.
92+
93+ * @param key_name The key for the gsettings-field that should contain
94+ * the saved-state. Default is "saved-state".
95+ */
96+ public SavedState (Gtk.Window window, string schema, string key_name = "saved-state") {
97+ backend = new SavedStateBackend (schema, key_name);
98+ this.window = window;
99+ window.delete_event.connect (() => {
100+ update_backend ();
101+ return false;
102+ });
103+
104+ // we update the window directly at the end of this constructor
105+ update_window ();
106+ }
107+
108+ /**
109+ * Updates the saved-state backend. You only need to call this method
110+ * if you expect that the app could quit without proper shutting down
111+ * the UI. This class will otherwise automatically call this function
112+ * when the window is deleted.
113+ */
114+ public void update_backend () {
115+ backend.state = get_window_state ();
116+
117+ int width, height;
118+ window.get_size (out width, out height);
119+ backend.width = width;
120+ backend.height = height;
121+
122+ int window_x, window_y;
123+ window.get_position (out window_x, out window_y);
124+ backend.x = window_x;
125+ backend.y = window_y;
126+
127+ backend.write ();
128+ }
129+
130+ /**
131+ * Updates the window with the values from the saved-state.
132+ * We keep this method private as saved-state is not supposed to react
133+ * to during-runtime changes to the gsettings-backend.
134+ */
135+ private void update_window () {
136+
137+ window.set_size_request (backend.width, backend.height);
138+
139+ if (backend.x != -1 && backend.y != -1) {
140+ window.move (backend.x, backend.y);
141+ }
142+
143+ if (backend.state == WindowState.MAXIMIZED
144+ || backend.state == WindowState.MAXIMIZED_FULLSCREEN) {
145+ if (get_window_state () != WindowState.MAXIMIZED) {
146+ window.maximize ();
147+ }
148+ }
149+ if (backend.state == WindowState.FULLSCREEN
150+ || backend.state == WindowState.MAXIMIZED_FULLSCREEN) {
151+ if (get_window_state () != WindowState.FULLSCREEN) {
152+ window.fullscreen ();
153+ }
154+ }
155+ if (backend.state == WindowState.NORMAL) {
156+ // In case the window is maximized or in fullscreen-mode
157+ // we bring it back as a normal window
158+ if (get_window_state () == WindowState.FULLSCREEN) {
159+ window.unfullscreen ();
160+ } else if (get_window_state () == WindowState.MAXIMIZED) {
161+ window.unmaximize ();
162+ }
163+ }
164+ }
165+
166+ private enum WindowState {
167+ NORMAL = 0,
168+ MAXIMIZED = 1,
169+ FULLSCREEN = 2,
170+ MAXIMIZED_FULLSCREEN = 3
171+ }
172+
173+ private WindowState get_window_state () {
174+ Gdk.Window gdk_window = window.get_window ();
175+ if ((gdk_window.get_state () & Gdk.WindowState.MAXIMIZED) != 0) {
176+ if ((gdk_window.get_state () & Gdk.WindowState.FULLSCREEN) != 0) {
177+ return WindowState.MAXIMIZED_FULLSCREEN;
178+ } else {
179+ return WindowState.MAXIMIZED;
180+ }
181+ } else if ((gdk_window.get_state () & Gdk.WindowState.FULLSCREEN) != 0) {
182+ return WindowState.FULLSCREEN;
183+ } else {
184+ return WindowState.NORMAL;
185+ }
186+ }
187+
188+ /**
189+ * Helper class to pass values to the gsettings-backend.
190+ */
191+ private class SavedStateBackend : Object{
192+
193+ public int x { get; set; }
194+ public int y { get; set; }
195+ public int width { get; set; }
196+ public int height { get; set; }
197+ public WindowState state { get; set; }
198+
199+ GLib.Settings settings;
200+
201+ public string key_name;
202+
203+ /**
204+ * Writes the values to the gsettings-backend.
205+ */
206+ public void write () {
207+ GLib.Variant v_x = new GLib.Variant.int32 (x);
208+ GLib.Variant v_y = new GLib.Variant.int32 (y);
209+ GLib.Variant v_width = new GLib.Variant.int32 (width);
210+ GLib.Variant v_height = new GLib.Variant.int32 (height);
211+ GLib.Variant v_state = new GLib.Variant.uint32 ((uint32) state);
212+ message (state.to_string ());
213+ // Tuple that contains the whole saved-state
214+ GLib.Variant tuple = new GLib.Variant.tuple ({v_x, v_y, v_width, v_height, v_state});
215+ settings.set_value (key_name, tuple);
216+ }
217+
218+ private void read () {
219+ GLib.Variant saved_state = settings.get_value (key_name);
220+ int out_x, out_y, out_w, out_h;
221+ uint out_state;
222+ saved_state.get ("(iiiiu)",
223+ out out_x,
224+ out out_y,
225+ out out_w,
226+ out out_h,
227+ out out_state);
228+
229+ // Check that we got a valid enum-value
230+ if (out_state > 3)
231+ out_state = 0;
232+
233+ x = out_x;
234+ y = out_y;
235+ width = out_w;
236+ height = out_h;
237+ state = (WindowState) out_state;
238+
239+ }
240+
241+ public SavedStateBackend (string schema, string key_name) {
242+ this.key_name = key_name;
243+ settings = new GLib.Settings (schema);
244+ read ();
245+ }
246+ }
247+
248+ }
249+
250+}

Subscribers

People subscribed via source and target branches