Merge lp:~tintou/granite/granite-headerbar into lp:~elementary-pantheon/granite/granite
- granite-headerbar
- Merge into granite
Proposed by
David Gomes
Status: | Rejected |
---|---|
Rejected by: | Corentin Noël |
Proposed branch: | lp:~tintou/granite/granite-headerbar |
Merge into: | lp:~elementary-pantheon/granite/granite |
Diff against target: |
702 lines (+454/-54) 8 files modified
CMakeLists.txt (+1/-1) cmake/FindGirCompiler.cmake (+56/-0) cmake/Tests.cmake (+5/-0) cmake/Translations.cmake (+2/-1) cmake/ValaPrecompile.cmake (+41/-6) demo/GraniteDemo.vala (+30/-35) lib/CMakeLists.txt (+5/-11) lib/Widgets/HeaderBar.vala (+314/-0) |
To merge this branch: | bzr merge lp:~tintou/granite/granite-headerbar |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Danielle Foré | Pending | ||
elementary Pantheon team | Pending | ||
Review via email: mp+199914@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Danielle Foré (danrabbit) wrote : | # |
Unmerged revisions
- 665. By Corentin Noël
-
Added valadoc
- 664. By Corentin Noël
-
Do not reparent widgets if they are already on the right container, check if the window is resizeable to show the maximize button
- 663. By Corentin Noël
-
Now fully working
- 662. By Corentin Noël
-
New HeaderBar widget
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2013-11-15 15:01:20 +0000 |
3 | +++ CMakeLists.txt 2013-12-22 03:34:36 +0000 |
4 | @@ -9,7 +9,7 @@ |
5 | set (API_VERSION 1.0) |
6 | |
7 | # Used to create GObject introspection files |
8 | -set (PKG_GIR_NAME Granite) |
9 | +set (PKG_GIR_NAME Granite-${PKG_VERSION}) |
10 | |
11 | list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) |
12 | |
13 | |
14 | === added file 'cmake/FindGirCompiler.cmake' |
15 | --- cmake/FindGirCompiler.cmake 1970-01-01 00:00:00 +0000 |
16 | +++ cmake/FindGirCompiler.cmake 2013-12-22 03:34:36 +0000 |
17 | @@ -0,0 +1,56 @@ |
18 | +## |
19 | +# Copyright 2009-2010 Jakob Westhoff. All rights reserved. |
20 | +# |
21 | +# Redistribution and use in source and binary forms, with or without |
22 | +# modification, are permitted provided that the following conditions are met: |
23 | +# |
24 | +# 1. Redistributions of source code must retain the above copyright notice, |
25 | +# this list of conditions and the following disclaimer. |
26 | +# |
27 | +# 2. Redistributions in binary form must reproduce the above copyright notice, |
28 | +# this list of conditions and the following disclaimer in the documentation |
29 | +# and/or other materials provided with the distribution. |
30 | +# |
31 | +# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR |
32 | +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
33 | +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
34 | +# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
35 | +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
36 | +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
37 | +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
38 | +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
39 | +# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
40 | +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
41 | +# |
42 | +# The views and conclusions contained in the software and documentation are those |
43 | +# of the authors and should not be interpreted as representing official policies, |
44 | +# either expressed or implied, of Jakob Westhoff |
45 | +## |
46 | + |
47 | +## |
48 | +# Find module for the Gir compiler (g-ir-compiler) |
49 | +# |
50 | +# This module determines wheter a Gir compiler is installed on the current |
51 | +# system and where its executable is. |
52 | +# |
53 | +# Call the module using "find_package(GirCompiler) from within your CMakeLists.txt. |
54 | +# |
55 | +# The following variables will be set after an invocation: |
56 | +# |
57 | +# G_IR_COMPILER_FOUND Whether the g-ir-compiler compiler has been found or not |
58 | +# G_IR_COMPILER_EXECUTABLE Full path to the g-ir-compiler executable if it has been found |
59 | +## |
60 | + |
61 | + |
62 | +# Search for the g-ir-compiler executable in the usual system paths. |
63 | +find_program (G_IR_COMPILER_EXECUTABLE |
64 | + NAMES g-ir-compiler) |
65 | + |
66 | +# Handle the QUIETLY and REQUIRED arguments, which may be given to the find call. |
67 | +# Furthermore set G_IR_COMPILER_FOUND to TRUE if the g-ir-compiler has been found (aka. |
68 | +# G_IR_COMPILER_EXECUTABLE is set) |
69 | + |
70 | +include (FindPackageHandleStandardArgs) |
71 | +find_package_handle_standard_args (GirCompiler DEFAULT_MSG G_IR_COMPILER_EXECUTABLE) |
72 | + |
73 | +mark_as_advanced (G_IR_COMPILER_EXECUTABLE) |
74 | |
75 | === added file 'cmake/Tests.cmake' |
76 | --- cmake/Tests.cmake 1970-01-01 00:00:00 +0000 |
77 | +++ cmake/Tests.cmake 2013-12-22 03:34:36 +0000 |
78 | @@ -0,0 +1,5 @@ |
79 | +# Test macros for Marlin, feel free to re-use them. |
80 | + |
81 | +macro(add_test_executable EXE_NAME) |
82 | + add_test(${EXE_NAME} gtester ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}) |
83 | +endmacro() |
84 | |
85 | === modified file 'cmake/Translations.cmake' (properties changed: -x to +x) |
86 | --- cmake/Translations.cmake 2012-06-05 05:05:44 +0000 |
87 | +++ cmake/Translations.cmake 2013-12-22 03:34:36 +0000 |
88 | @@ -36,6 +36,7 @@ |
89 | |
90 | add_custom_command (TARGET pot COMMAND |
91 | ${XGETTEXT_EXECUTABLE} -d ${NLS_PACKAGE} -o ${CMAKE_CURRENT_SOURCE_DIR}/${NLS_PACKAGE}.pot |
92 | - ${VALA_SOURCE} ${C_SOURCE} --keyword="_" --keyword="N_" --from-code=UTF-8 |
93 | + ${VALA_SOURCE} ${C_SOURCE} --add-comments="/" --keyword="_" --keyword="N_" --keyword="C_:1c,2" |
94 | + --keyword="NC_:1c,2" --keyword="ngettext:1,2" --keyword="N_" --keyword="Q_:1g" --from-code=UTF-8 |
95 | ) |
96 | endmacro() |
97 | |
98 | === modified file 'cmake/ValaPrecompile.cmake' |
99 | --- cmake/ValaPrecompile.cmake 2012-11-28 02:35:13 +0000 |
100 | +++ cmake/ValaPrecompile.cmake 2013-12-22 03:34:36 +0000 |
101 | @@ -1,5 +1,6 @@ |
102 | ## |
103 | # Copyright 2009-2010 Jakob Westhoff. All rights reserved. |
104 | +# Copyright 2012 elementary. |
105 | # |
106 | # Redistribution and use in source and binary forms, with or without |
107 | # modification, are permitted provided that the following conditions are met: |
108 | @@ -130,20 +131,37 @@ |
109 | set(out_files "") |
110 | set(out_files_display "") |
111 | set(${output} "") |
112 | + |
113 | foreach(src ${ARGS_DEFAULT_ARGS}) |
114 | - list(APPEND in_files "${CMAKE_CURRENT_SOURCE_DIR}/${src}") |
115 | + string(REGEX MATCH "^/" IS_MATCHED ${src}) |
116 | + if(${IS_MATCHED} MATCHES "/") |
117 | + set(src_file_path ${src}) |
118 | + else() |
119 | + set(src_file_path ${CMAKE_CURRENT_SOURCE_DIR}/${src}) |
120 | + endif() |
121 | + list(APPEND in_files ${src_file_path}) |
122 | string(REPLACE ".vala" ".c" src ${src}) |
123 | string(REPLACE ".gs" ".c" src ${src}) |
124 | - set(out_file "${DIRECTORY}/${src}") |
125 | - list(APPEND out_files "${DIRECTORY}/${src}") |
126 | + if(${IS_MATCHED} MATCHES "/") |
127 | + get_filename_component(VALA_FILE_NAME ${src} NAME) |
128 | + set(out_file "${CMAKE_CURRENT_BINARY_DIR}/${VALA_FILE_NAME}") |
129 | + list(APPEND out_files "${CMAKE_CURRENT_BINARY_DIR}/${VALA_FILE_NAME}") |
130 | + else() |
131 | + set(out_file "${DIRECTORY}/${src}") |
132 | + list(APPEND out_files "${DIRECTORY}/${src}") |
133 | + endif() |
134 | + list(APPEND ${output} ${out_file}) |
135 | list(APPEND out_files_display "${src}") |
136 | - list(APPEND ${output} ${out_file}) |
137 | endforeach(src ${ARGS_DEFAULT_ARGS}) |
138 | |
139 | set(custom_vapi_arguments "") |
140 | if(ARGS_CUSTOM_VAPIS) |
141 | foreach(vapi ${ARGS_CUSTOM_VAPIS}) |
142 | - list(APPEND custom_vapi_arguments ${vapi}) |
143 | + if(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR}) |
144 | + list(APPEND custom_vapi_arguments ${vapi}) |
145 | + else (${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR}) |
146 | + list(APPEND custom_vapi_arguments ${CMAKE_CURRENT_SOURCE_DIR}/${vapi}) |
147 | + endif(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR}) |
148 | endforeach(vapi ${ARGS_CUSTOM_VAPIS}) |
149 | endif(ARGS_CUSTOM_VAPIS) |
150 | |
151 | @@ -152,6 +170,11 @@ |
152 | list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_VAPI}.vapi") |
153 | list(APPEND out_files_display "${ARGS_GENERATE_VAPI}.vapi") |
154 | set(vapi_arguments "--library=${ARGS_GENERATE_VAPI}" "--vapi=${ARGS_GENERATE_VAPI}.vapi") |
155 | + |
156 | + # Header and internal header is needed to generate internal vapi |
157 | + if (NOT ARGS_GENERATE_HEADER) |
158 | + set(ARGS_GENERATE_HEADER ${ARGS_GENERATE_VAPI}) |
159 | + endif(NOT ARGS_GENERATE_HEADER) |
160 | endif(ARGS_GENERATE_VAPI) |
161 | |
162 | set(header_arguments "") |
163 | @@ -162,10 +185,21 @@ |
164 | endif(ARGS_GENERATE_HEADER) |
165 | |
166 | set(gir_arguments "") |
167 | + set(gircomp_command "") |
168 | if(ARGS_GENERATE_GIR) |
169 | list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_GIR}.gir") |
170 | list(APPEND out_files_display "${ARGS_GENERATE_GIR}.gir") |
171 | set(gir_arguments "--gir=${ARGS_GENERATE_GIR}.gir") |
172 | + |
173 | + include (FindGirCompiler) |
174 | + find_package(GirCompiler REQUIRED) |
175 | + |
176 | + set(gircomp_command |
177 | + COMMAND |
178 | + ${G_IR_COMPILER_EXECUTABLE} |
179 | + ARGS |
180 | + "${DIRECTORY}/${ARGS_GENERATE_GIR}.gir" |
181 | + -o "${DIRECTORY}/${ARGS_GENERATE_GIR}.typelib") |
182 | endif(ARGS_GENERATE_GIR) |
183 | |
184 | set(symbols_arguments "") |
185 | @@ -178,7 +212,7 @@ |
186 | # Workaround for a bug that would make valac run twice. This file is written |
187 | # after the vala compiler generates C source code. |
188 | set(OUTPUT_STAMP ${CMAKE_CURRENT_BINARY_DIR}/${target_name}_valac.stamp) |
189 | - |
190 | + |
191 | add_custom_command( |
192 | OUTPUT |
193 | ${OUTPUT_STAMP} |
194 | @@ -205,6 +239,7 @@ |
195 | ${ARGS_CUSTOM_VAPIS} |
196 | COMMENT |
197 | "Generating ${out_files_display}" |
198 | + ${gircomp_command} |
199 | ) |
200 | |
201 | # This command will be run twice for some reason (pass a non-empty string to COMMENT |
202 | |
203 | === modified file 'demo/GraniteDemo.vala' |
204 | --- demo/GraniteDemo.vala 2013-12-15 12:41:49 +0000 |
205 | +++ demo/GraniteDemo.vala 2013-12-22 03:34:36 +0000 |
206 | @@ -84,6 +84,7 @@ |
207 | private Gtk.Grid main_layout; // outer-most container |
208 | private Granite.Widgets.ModeButton mode_button; |
209 | private int dark_mode_index; |
210 | + private bool fullscreen = false; |
211 | |
212 | /** |
213 | * Basic app information for Granite.Application. This is used by the About dialog. |
214 | @@ -125,10 +126,8 @@ |
215 | |
216 | this.add_window (window); |
217 | |
218 | - var main_toolbar = new Gtk.Toolbar (); |
219 | - main_toolbar.get_style_context ().add_class (Gtk.STYLE_CLASS_PRIMARY_TOOLBAR); |
220 | - main_toolbar.hexpand = true; |
221 | - main_toolbar.vexpand = false; |
222 | + var main_toolbar = new Granite.Widgets.HeaderBar (window); |
223 | + main_toolbar.set_title ("Demo"); |
224 | |
225 | // SourceList |
226 | var sidebar = new Granite.Widgets.SourceList (); |
227 | @@ -169,7 +168,7 @@ |
228 | main_layout = new Gtk.Grid (); |
229 | main_layout.expand = true; |
230 | main_layout.orientation = Gtk.Orientation.VERTICAL; |
231 | - main_layout.add (main_toolbar); |
232 | + main_layout.add (main_toolbar.get_revealer ()); |
233 | main_layout.add (sidebar_paned); |
234 | main_layout.add (statusbar); |
235 | |
236 | @@ -185,14 +184,12 @@ |
237 | sidebar.selected = welcome_item; |
238 | |
239 | // Light window |
240 | - var light_window_icon = new Gtk.Image.from_icon_name ("document-new", Gtk.IconSize.LARGE_TOOLBAR); |
241 | - var light_window_item = new Gtk.ToolButton (light_window_icon, "Show LightWindow"); |
242 | - light_window_item.icon_name = "document-new"; |
243 | + var light_window_item = new Gtk.Button.from_icon_name ("document-new", Gtk.IconSize.LARGE_TOOLBAR); |
244 | light_window_item.tooltip_text = "Show Light Window"; |
245 | light_window_item.halign = light_window_item.valign = Gtk.Align.CENTER; |
246 | light_window_item.clicked.connect (show_light_window); |
247 | |
248 | - main_toolbar.insert (light_window_item, -1); |
249 | + main_toolbar.pack_start (light_window_item); |
250 | |
251 | // StaticNotebook |
252 | var staticnotebook = new Granite.Widgets.StaticNotebook (); |
253 | @@ -224,11 +221,7 @@ |
254 | |
255 | mode_button.mode_changed.connect (on_theme_mode_button_changed); |
256 | |
257 | - var mode_button_item = new Gtk.ToolItem (); |
258 | - mode_button_item.add (mode_button); |
259 | - main_toolbar.insert (mode_button_item, -1); |
260 | - |
261 | - mode_button_item.halign = mode_button_item.valign = Gtk.Align.CENTER; |
262 | + main_toolbar.pack_start (mode_button); |
263 | |
264 | // PopOvers |
265 | var popover_statusbar_item = new Gtk.Button (); |
266 | @@ -263,19 +256,14 @@ |
267 | }); |
268 | |
269 | // Date widget |
270 | - var calendar_tool_item = new Gtk.ToolItem (); |
271 | - calendar_tool_item.margin_left = 12; |
272 | var date_button = new Granite.Widgets.DatePicker.with_format ("%d-%m-%y"); |
273 | - calendar_tool_item.add (date_button); |
274 | - main_toolbar.insert (calendar_tool_item, -1); |
275 | + date_button.margin_left = 12; |
276 | + main_toolbar.pack_start (date_button); |
277 | |
278 | // Time widget |
279 | - var time_tool_item = new Gtk.ToolItem (); |
280 | - time_tool_item.margin_left = 12; |
281 | - time_tool_item.valign = Gtk.Align.CENTER; |
282 | var time_button = new Granite.Widgets.TimePicker (); |
283 | - time_tool_item.add (time_button); |
284 | - main_toolbar.insert (time_tool_item, -1); |
285 | + time_button.margin_left = 12; |
286 | + main_toolbar.pack_start (time_button); |
287 | |
288 | // Dynamic notebook |
289 | var dynamic_notebook = create_dynamic_notebook (); |
290 | @@ -283,22 +271,29 @@ |
291 | widgets_category.add (dynamic_notebook_item); |
292 | dynamic_notebook_item.page_num = page_switcher.append_page (dynamic_notebook, null); |
293 | |
294 | - var right_sep = new Gtk.SeparatorToolItem (); |
295 | - right_sep.draw = false; |
296 | - right_sep.set_expand (true); |
297 | - main_toolbar.insert (right_sep, -1); |
298 | - |
299 | // Search Entry |
300 | var search_entry = new Granite.Widgets.SearchBar ("Search"); |
301 | - var search_item = new Gtk.ToolItem (); |
302 | - search_item.add (search_entry); |
303 | - search_item.margin_left = 12; |
304 | - main_toolbar.insert (search_item, -1); |
305 | + search_entry.margin_left = 12; |
306 | + main_toolbar.pack_end (search_entry); |
307 | |
308 | // App Menu (this gives access to the About dialog) |
309 | - var main_menu = create_appmenu (new Gtk.Menu ()); |
310 | + var menu = new Gtk.Menu (); |
311 | + var fullscreen_item = new Gtk.MenuItem.with_label ("Fullscreen"); |
312 | + fullscreen_item.activate.connect (() => { |
313 | + if (fullscreen == false) { |
314 | + window.fullscreen (); |
315 | + fullscreen = true; |
316 | + } else { |
317 | + window.unfullscreen (); |
318 | + fullscreen = false; |
319 | + } |
320 | + |
321 | + }); |
322 | + menu.add (fullscreen_item); |
323 | + |
324 | + var main_menu = create_appmenu (menu); |
325 | main_menu.margin_left = 12; |
326 | - main_toolbar.insert (main_menu, -1); |
327 | + main_toolbar.pack_end (main_menu); |
328 | |
329 | window.set_default_size (800, 550); |
330 | window.show_all (); |
331 | @@ -464,4 +459,4 @@ |
332 | |
333 | return application.run (args); |
334 | } |
335 | -} |
336 | +} |
337 | \ No newline at end of file |
338 | |
339 | === modified file 'lib/CMakeLists.txt' |
340 | --- lib/CMakeLists.txt 2013-07-16 23:31:04 +0000 |
341 | +++ lib/CMakeLists.txt 2013-12-22 03:34:36 +0000 |
342 | @@ -31,6 +31,7 @@ |
343 | Widgets/DynamicNotebook.vala |
344 | Widgets/CompositedWindow.vala |
345 | Widgets/AppMenu.vala |
346 | + Widgets/HeaderBar.vala |
347 | Widgets/Welcome.vala |
348 | Widgets/ToolButtonWithMenu.vala |
349 | Widgets/PopOver.vala |
350 | @@ -84,6 +85,8 @@ |
351 | ${PKG_NAME} |
352 | GENERATE_VAPI |
353 | ${PKG_NAME} |
354 | +GENERATE_GIR |
355 | + ${PKG_GIR_NAME} |
356 | CUSTOM_VAPIS |
357 | ${CMAKE_CURRENT_SOURCE_DIR}/config.vapi |
358 | OPTIONS |
359 | @@ -94,6 +97,8 @@ |
360 | install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/${PKG_NAME}.deps DESTINATION ${CMAKE_INSTALL_PREFIX}/share/vala/vapi) |
361 | install (FILES ${CMAKE_CURRENT_BINARY_DIR}/${PKG_NAME}.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${PKG_NAME}) |
362 | install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/Widgets/widgets-utils.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/${PKG_NAME}) |
363 | +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/${PKG_GIR_NAME}.gir DESTINATION ${CMAKE_INSTALL_PREFIX}/share/gir-1.0/) |
364 | +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/${PKG_GIR_NAME}.typelib DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/girepository-1.0/) |
365 | |
366 | set (LIB_FILES ${C_SOURCES} ${VALA_C}) |
367 | |
368 | @@ -106,14 +111,3 @@ |
369 | SOVERSION ${PKG_SOVERSION}) |
370 | |
371 | install (TARGETS ${PKG_NAME} DESTINATION ${LIB_INSTALL_DIR}) |
372 | - |
373 | -########################### |
374 | -# GObject Introspection |
375 | -########################### |
376 | - |
377 | -include (FindGObjectIntrospection) |
378 | - |
379 | -if (INTROSPECTION_FOUND) |
380 | - include (GObjectIntrospectionMacros) |
381 | - add_target_gir (${PKG_NAME} ${PKG_GIR_NAME} ${PKG_NAME}.h "${VALA_C}" "${DEPS_CFLAGS}" ${API_VERSION} ${GI_PKG_DEPS}) |
382 | -endif () |
383 | |
384 | === added file 'lib/Widgets/HeaderBar.vala' |
385 | --- lib/Widgets/HeaderBar.vala 1970-01-01 00:00:00 +0000 |
386 | +++ lib/Widgets/HeaderBar.vala 2013-12-22 03:34:36 +0000 |
387 | @@ -0,0 +1,314 @@ |
388 | +/*** |
389 | + Copyright (C) 2011-2013 Granite Developers |
390 | + |
391 | + This program or library is free software; you can redistribute it |
392 | + and/or modify it under the terms of the GNU Lesser General Public |
393 | + License as published by the Free Software Foundation; either |
394 | + version 3 of the License, or (at your option) any later version. |
395 | + |
396 | + This library is distributed in the hope that it will be useful, |
397 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
398 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
399 | + Lesser General Public License for more details. |
400 | + |
401 | + You should have received a copy of the GNU Lesser General |
402 | + Public License along with this library; if not, write to the |
403 | + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
404 | + Boston, MA 02110-1301 USA. |
405 | + |
406 | + Authored by: Corentin Noël <tintou@mailoo.org> |
407 | +***/ |
408 | + |
409 | +namespace Granite.Widgets { |
410 | + |
411 | + /** |
412 | + * This class is a standard header bar. |
413 | + */ |
414 | + public class HeaderBar : Gtk.HeaderBar { |
415 | + |
416 | + // Same value as int the Gtk+3 source |
417 | + private const int DEFAULT_SPACING = 6; |
418 | + |
419 | + private Gtk.Revealer revealer; |
420 | + private Gtk.Window window; |
421 | + private Gtk.Toolbar toolbar; |
422 | + private Gee.HashMap<int, unowned Gtk.Widget> widgets_left; |
423 | + private Gee.HashMap<int, unowned Gtk.Widget> widgets_right; |
424 | + private int left_size = 0; |
425 | + private int right_size = 0; |
426 | + private Gtk.Grid left_grid_toolbar; |
427 | + private Gtk.Grid right_grid_toolbar; |
428 | + private Gtk.Grid widget_grid_toolbar; |
429 | + private Gtk.Grid left_grid_header; |
430 | + private Gtk.Grid right_grid_header; |
431 | + private Gtk.Grid widget_grid_header; |
432 | + private Gtk.Button resize_button; |
433 | + private Gtk.Widget header_center_widget; |
434 | + |
435 | + private bool toolbar_shown = false; |
436 | + private bool widgets_on_toolbar = false; |
437 | + |
438 | + /** |
439 | + * This method sets an HeaderBar to the window |
440 | + * |
441 | + * @param window window to set the HeaderBar |
442 | + * @since 0.3 |
443 | + */ |
444 | + public HeaderBar (Gtk.Window window) { |
445 | + |
446 | + widgets_left = new Gee.HashMap<int, unowned Gtk.Widget> (); |
447 | + widgets_right = new Gee.HashMap<int, unowned Gtk.Widget> (); |
448 | + |
449 | + show_close_button = true; |
450 | + |
451 | + this.window = window; |
452 | + window.set_titlebar (this); |
453 | + this.get_style_context ().add_class ("header-bar"); |
454 | + this.get_style_context ().add_class (Gtk.STYLE_CLASS_HORIZONTAL); |
455 | + |
456 | + revealer = new Gtk.Revealer (); |
457 | + toolbar = new Gtk.Toolbar (); |
458 | + toolbar.get_style_context ().add_class ("header-bar"); |
459 | + toolbar.hexpand = true; |
460 | + toolbar.vexpand = false; |
461 | + |
462 | + left_grid_toolbar = new Gtk.Grid (); |
463 | + left_grid_toolbar.get_style_context ().add_class ("header-bar"); |
464 | + left_grid_toolbar.column_spacing = DEFAULT_SPACING; |
465 | + right_grid_toolbar = new Gtk.Grid (); |
466 | + right_grid_toolbar.get_style_context ().add_class ("header-bar"); |
467 | + right_grid_toolbar.column_spacing = DEFAULT_SPACING; |
468 | + |
469 | + widget_grid_toolbar = new Gtk.Grid (); |
470 | + header_center_widget = custom_title; |
471 | + widget_grid_header = new Gtk.Grid (); |
472 | + |
473 | + left_grid_header = new Gtk.Grid (); |
474 | + left_grid_header.expand = true; |
475 | + left_grid_header.get_style_context ().add_class ("header-bar"); |
476 | + left_grid_header.column_spacing = DEFAULT_SPACING; |
477 | + base.pack_start (left_grid_header); |
478 | + right_grid_header = new Gtk.Grid (); |
479 | + right_grid_header.expand = true; |
480 | + right_grid_header.get_style_context ().add_class ("header-bar"); |
481 | + right_grid_header.column_spacing = DEFAULT_SPACING; |
482 | + base.pack_end (right_grid_header); |
483 | + |
484 | + get_resize_button (); |
485 | + |
486 | + var left_toolitem = new Gtk.ToolItem (); |
487 | + left_toolitem.add (left_grid_toolbar); |
488 | + |
489 | + var widget_toolitem = new Gtk.ToolItem (); |
490 | + widget_toolitem.add (widget_grid_toolbar); |
491 | + |
492 | + var right_toolitem = new Gtk.ToolItem (); |
493 | + right_toolitem.add (right_grid_toolbar); |
494 | + |
495 | + var fake_toolitem = new Gtk.ToolItem (); |
496 | + fake_toolitem.set_expand (true); |
497 | + fake_toolitem.hexpand = false; |
498 | + |
499 | + var fake2_toolitem = new Gtk.ToolItem (); |
500 | + fake2_toolitem.set_expand (true); |
501 | + fake2_toolitem.hexpand = false; |
502 | + |
503 | + toolbar.insert (fake_toolitem, 0); |
504 | + toolbar.insert (widget_toolitem, 0); |
505 | + toolbar.insert (fake2_toolitem, 0); |
506 | + toolbar.insert (left_toolitem, 0); |
507 | + toolbar.insert (right_toolitem, -1); |
508 | + |
509 | + revealer.add (toolbar); |
510 | + |
511 | + window.add_events (Gdk.EventMask.STRUCTURE_MASK); |
512 | + window.add_events (Gdk.EventMask.POINTER_MOTION_MASK); |
513 | + |
514 | + window.motion_notify_event.connect (update_pointer_position); |
515 | + window.window_state_event.connect (got_window_state_event); |
516 | + } |
517 | + |
518 | + private bool got_window_state_event (Gdk.EventWindowState event) { |
519 | + if (toolbar_shown == true && |
520 | + (((window.get_window ().get_state () & Gdk.WindowState.FULLSCREEN) != 0) == false)) { |
521 | + hide_revealer (); |
522 | + revealer.hide (); |
523 | + } |
524 | + if ((((window.get_window ().get_state () & Gdk.WindowState.FULLSCREEN) != 0) == false) && |
525 | + ((window.get_window ().get_state () & Gdk.WindowState.MAXIMIZED) != 0)) { |
526 | + toggle_maximized_button (true); |
527 | + } else if ((((window.get_window ().get_state () & Gdk.WindowState.FULLSCREEN) != 0) == false) && |
528 | + (((window.get_window ().get_state () & Gdk.WindowState.MAXIMIZED) != 0)) == false) { |
529 | + toggle_maximized_button (false); |
530 | + } |
531 | + return false; |
532 | + } |
533 | + |
534 | + private void get_resize_button () { |
535 | + bool maximized = ((window.get_window ().get_state () & Gdk.WindowState.MAXIMIZED) != 0); |
536 | + |
537 | + var icon_name = maximized ? "window-restore-symbolic" : "window-maximize-symbolic"; |
538 | + resize_button = new Gtk.Button.from_icon_name (icon_name, Gtk.IconSize.MENU); |
539 | + resize_button.get_style_context ().add_class ("titlebutton"); |
540 | + resize_button.set_can_focus (false); |
541 | + resize_button.clicked.connect (toggle_maximized); |
542 | + resize_button.set_valign (Gtk.Align.CENTER); |
543 | + |
544 | + if (window.resizable == true) { |
545 | + resize_button.show (); |
546 | + } else { |
547 | + resize_button.hide (); |
548 | + } |
549 | + |
550 | + base.pack_end (resize_button); |
551 | + } |
552 | + |
553 | + private void toggle_maximized () { |
554 | + if (((window.get_window ().get_state () & Gdk.WindowState.MAXIMIZED) != 0)) { |
555 | + window.unmaximize (); |
556 | + } else { |
557 | + window.maximize (); |
558 | + } |
559 | + } |
560 | + |
561 | + private void toggle_maximized_button (bool maximized) { |
562 | + |
563 | + if (window.resizable == true) { |
564 | + resize_button.show (); |
565 | + } else { |
566 | + resize_button.hide (); |
567 | + } |
568 | + |
569 | + if (maximized) { |
570 | + resize_button.set_image (new Gtk.Image.from_icon_name ("window-restore-symbolic", Gtk.IconSize.MENU)); |
571 | + } else { |
572 | + resize_button.set_image (new Gtk.Image.from_icon_name ("window-maximize-symbolic", Gtk.IconSize.MENU)); |
573 | + } |
574 | + } |
575 | + |
576 | + private bool update_pointer_position (Gdk.EventMotion event) { |
577 | + int size = 5; |
578 | + if (toolbar_shown == true) { |
579 | + int minimum_height = 0; |
580 | + int natural_height = 0; |
581 | + toolbar.get_preferred_height (out minimum_height, out natural_height); |
582 | + size = natural_height; |
583 | + if (size < 5) |
584 | + size = 5; |
585 | + } |
586 | + |
587 | + if (toolbar_shown == false && event.y <= size && |
588 | + ((window.get_window ().get_state () & Gdk.WindowState.FULLSCREEN) != 0)) { |
589 | + revealer.show (); |
590 | + show_revealer (); |
591 | + } else if (toolbar_shown == true && event.y > size && |
592 | + ((window.get_window ().get_state () & Gdk.WindowState.FULLSCREEN) != 0)) { |
593 | + revealer.show (); |
594 | + hide_revealer (); |
595 | + } else if (toolbar_shown == true && |
596 | + (((window.get_window ().get_state () & Gdk.WindowState.FULLSCREEN) != 0) == false)) { |
597 | + hide_revealer (); |
598 | + revealer.hide (); |
599 | + } |
600 | + return false; |
601 | + } |
602 | + |
603 | + private void show_revealer () { |
604 | + if (widgets_on_toolbar == false) { |
605 | + foreach (var entry in widgets_left.entries) { |
606 | + if (entry.value == null) |
607 | + continue; |
608 | + entry.value.reparent (left_grid_toolbar); |
609 | + } |
610 | + foreach (var entry in widgets_right.entries) { |
611 | + if (entry.value == null) |
612 | + continue; |
613 | + entry.value.reparent (right_grid_toolbar); |
614 | + } |
615 | + if (header_center_widget != null) |
616 | + header_center_widget.reparent (widget_grid_toolbar); |
617 | + widgets_on_toolbar = true; |
618 | + } |
619 | + revealer.set_transition_type (Gtk.RevealerTransitionType.SLIDE_DOWN); |
620 | + toolbar_shown = true; |
621 | + revealer.set_reveal_child (true); |
622 | + } |
623 | + |
624 | + private void hide_revealer () { |
625 | + if (widgets_on_toolbar == true) { |
626 | + foreach (var entry in widgets_left.entries) { |
627 | + if (entry.value == null) |
628 | + continue; |
629 | + entry.value.reparent (left_grid_header); |
630 | + } |
631 | + foreach (var entry in widgets_right.entries) { |
632 | + if (entry.value == null) |
633 | + continue; |
634 | + entry.value.reparent (right_grid_header); |
635 | + } |
636 | + if (header_center_widget != null) |
637 | + header_center_widget.reparent (widget_grid_header); |
638 | + widgets_on_toolbar = false; |
639 | + } |
640 | + revealer.set_transition_type (Gtk.RevealerTransitionType.SLIDE_UP); |
641 | + toolbar_shown = false; |
642 | + revealer.set_reveal_child (false); |
643 | + } |
644 | + |
645 | + /** |
646 | + * Get the Toolbar that will be shown in fullscreen mode. |
647 | + * |
648 | + * @return A {@link Gtk.Revealer} that should be set in the top of the window, expanded horizontally. |
649 | + * @since 0.3 |
650 | + */ |
651 | + public Gtk.Revealer get_revealer () { |
652 | + return revealer; |
653 | + } |
654 | + |
655 | + /** |
656 | + * {@inheritDoc} |
657 | + * @since 0.3 |
658 | + */ |
659 | + public new weak Gtk.Widget get_custom_title () { |
660 | + return header_center_widget; |
661 | + } |
662 | + |
663 | + /** |
664 | + * {@inheritDoc} |
665 | + * @since 0.3 |
666 | + */ |
667 | + public new void set_custom_title (Gtk.Widget title_widget) { |
668 | + widget_grid_header = new Gtk.Grid (); |
669 | + widget_grid_header.add (title_widget); |
670 | + header_center_widget = title_widget; |
671 | + if (title_widget == null) |
672 | + base.set_custom_title (null); |
673 | + else |
674 | + base.set_custom_title (widget_grid_header); |
675 | + } |
676 | + |
677 | + /** |
678 | + * {@inheritDoc} |
679 | + * @since 0.3 |
680 | + */ |
681 | + public new void pack_end (Gtk.Widget child) { |
682 | + widgets_right.set (right_size, child); |
683 | + child.valign = Gtk.Align.CENTER; |
684 | + child.vexpand = true; |
685 | + right_grid_header.attach (child, right_size, 0, 1, 1); |
686 | + right_size++; |
687 | + } |
688 | + |
689 | + /** |
690 | + * {@inheritDoc} |
691 | + * @since 0.3 |
692 | + */ |
693 | + public new void pack_start (Gtk.Widget child) { |
694 | + widgets_left.set (left_size, child); |
695 | + child.valign = Gtk.Align.CENTER; |
696 | + child.vexpand = true; |
697 | + left_grid_header.attach (child, left_size, 0, 1, 1); |
698 | + left_size++; |
699 | + } |
700 | + } |
701 | +} |
702 | \ No newline at end of file |
After some discussion, I think we need to first decide if we intend to ship with gtk 3.12 or backport any features of gtk 3.12 (like max and min buttons) which would make this branch obsolete.
IMO, the latest improvements to headerbar would be worth it.