1# deco_manager.py
2#
3# Copyright 2018-2021 Romain F. T.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18from gi.repository import Gtk
19
20class DrDecoManagerMenubar():
21	__gtype_name__ = 'DrDecoManagerMenubar'
22	UI_PATH = '/com/github/maoschanz/drawing/ui/'
23
24	def __init__(self, window, use_menubar):
25		window.set_show_menubar(use_menubar)
26		self._window = window
27		self._main_menu_btn = None
28		if use_menubar:
29			window.set_titlebar(None) # that's an arbitrary restriction
30
31	def remove_from_ui(self):
32		return False
33
34	############################################################################
35
36	def set_titles(self, title_label, subtitle_label):
37		full_title = _("Drawing") + ' ~ ' + title_label + ' ~ ' + subtitle_label
38		self._window.set_title(full_title)
39
40	def toggle_menu(self):
41		if self._main_menu_btn is not None:
42			self._main_menu_btn.set_active(not self._main_menu_btn.get_active())
43
44	def set_undo_label(self, label):
45		pass # TODO update "undo" item in the menubar
46
47	def set_redo_label(self, label):
48		pass # TODO update "redo" item in the menubar
49
50	############################################################################
51	# Adaptability #############################################################
52
53	def init_adaptability(self):
54		pass
55
56	def adapt_to_window_size(self):
57		pass
58
59	def set_compact(self, state):
60		pass
61
62	############################################################################
63################################################################################
64
65class DrDecoManagerHeaderbar(DrDecoManagerMenubar):
66	__gtype_name__ = 'DrDecoManagerHeaderbar'
67
68	def __init__(self, is_eos, window):
69		super().__init__(window, False)
70		self._is_narrow = True # This is reducing the complexity of resizing,
71		# but its main goal is to avoid a GTK minor bug where the initial
72		# bunch of configure-event signals was sent to soon.
73
74		# Build the window's headerbar. If "is_eos" is true, the headerbar will
75		# follow elementaryOS guidelines, else it will follow GNOME guidelines.
76		if is_eos:
77			resource_path = self.UI_PATH + 'headerbar-eos.ui'
78		else:
79			resource_path = self.UI_PATH + 'headerbar.ui'
80		builder = Gtk.Builder.new_from_resource(resource_path)
81		self._widget = builder.get_object('header_bar')
82		window.set_titlebar(self._widget)
83
84		# Code differences are kept minimal between the 2 cases: widgets will
85		# share similar names in order to both work with the same method
86		# updating widgets' visibility when resizing.
87		self._save_long = builder.get_object('save_long')
88		self._save_short = builder.get_object('save_short')
89		self._hidable_widget_1 = builder.get_object('hidable1')
90		self._hidable_widget_2 = builder.get_object('hidable2')
91
92		# Mandatory widget name, used for the `win.main_menu` action
93		self._main_menu_btn = builder.get_object('main_menu_btn')
94
95		# History buttons whose tooltips depends on the last operation
96		self._undo_btn = builder.get_object('undo_btn')
97		self._redo_btn = builder.get_object('redo_btn')
98
99		# Quite extreme as a precaution, will be more precise later
100		self._limit_size = 750
101		self._manual_correction = 0
102
103		builder.add_from_resource(self.UI_PATH + 'win-menus.ui')
104		if is_eos:
105			self._init_menus_eos(builder)
106		else:
107			self._init_menus_gnome(builder)
108
109		# The longer one is set by default to be consistent with the initial
110		# value of self._is_narrow
111		self._main_menu_btn.set_menu_model(self._long_primary_menu)
112
113	def _init_menus_gnome(self, builder):
114		"""Sets the menus for the GNOME/Budgie layout: `self._hidable_widget_2`
115		is the "New Image" button here."""
116		self._short_primary_menu = builder.get_object('short-window-menu')
117		self._long_primary_menu = builder.get_object('long-window-menu')
118		self._hidable_widget_2.set_menu_model(builder.get_object('new-image-menu'))
119
120	def _init_menus_eos(self, builder):
121		"""Sets the menus for the Pantheon layout: the "New Image" button isn't
122		hidden here, and menus are shorter."""
123		self._short_primary_menu = builder.get_object('minimal-window-menu')
124		self._long_primary_menu = builder.get_object('short-window-menu')
125		save_as_menubtn = builder.get_object('hidable1')
126		save_as_menubtn.set_menu_model(builder.get_object('save-section'))
127		share_menubtn = builder.get_object('hidable2')
128		share_menubtn.set_menu_model(builder.get_object('share-section'))
129		new_btn = builder.get_object('new_btn')
130		new_btn.set_menu_model(builder.get_object('new-image-menu'))
131		self._manual_correction = -50
132
133	def remove_from_ui(self):
134		return self._is_narrow
135
136	############################################################################
137
138	def set_titles(self, title_label, subtitle_label):
139		super().set_titles(title_label, subtitle_label)
140		self._widget.set_title(title_label)
141		self._widget.set_subtitle(subtitle_label)
142
143	def set_undo_label(self, label):
144		super().set_undo_label(label)
145		if label is None:
146			self._undo_btn.set_tooltip_text(_("Undo"))
147		else:
148			self._undo_btn.set_tooltip_text(_("Undo %s") % label)
149
150	def set_redo_label(self, label):
151		super().set_redo_label(label)
152		if label is None:
153			self._redo_btn.set_tooltip_text(_("Redo"))
154		else:
155			self._redo_btn.set_tooltip_text(_("Redo %s") % label)
156
157	############################################################################
158	# Adaptability #############################################################
159
160	def init_adaptability(self):
161		# Header bar width limit
162		self._widget.show_all()
163		widgets_width = self._hidable_widget_1.get_preferred_width()[0] \
164		              + self._hidable_widget_2.get_preferred_width()[0] \
165		                     + self._save_long.get_preferred_width()[0] \
166		                    - self._save_short.get_preferred_width()[0] \
167		                      + self._undo_btn.get_preferred_width()[0] \
168		                      + self._redo_btn.get_preferred_width()[0]
169		widgets_width = widgets_width + self._manual_correction
170		self._limit_size = widgets_width * 2.5 # 100% arbitrary
171		# print(self._limit_size)
172		self.set_compact(True)
173		self.adapt_to_window_size()
174
175	def adapt_to_window_size(self):
176		can_expand = (self._widget.get_allocated_width() > self._limit_size)
177		incoherent = (can_expand == self._is_narrow)
178		if incoherent:
179			self.set_compact(not self._is_narrow)
180
181	def set_compact(self, state):
182		"""Set the compactness of the headerbar: if the parameter is True, wide
183		widgets will be hidden in favor of narrow ones. Else, the opposite."""
184		# Instead of a boolean, `state` could be an integer, which would be
185		# far more complex to handle, but would allow thinner granularity.
186		if state:
187			self._main_menu_btn.set_menu_model(self._long_primary_menu)
188		else:
189			self._main_menu_btn.set_menu_model(self._short_primary_menu)
190		self._save_long.set_visible(not state)
191		self._save_short.set_visible(state)
192		self._hidable_widget_1.set_visible(not state)
193		self._hidable_widget_2.set_visible(not state)
194		self._is_narrow = state
195
196	############################################################################
197################################################################################
198
199class DrDecoManagerToolbar(DrDecoManagerMenubar):
200	__gtype_name__ = 'DrDecoManagerToolbar'
201
202	def __init__(self, is_symbolic, with_menubar, window):
203		super().__init__(window, with_menubar)
204		window.set_titlebar(None) # that's an arbitrary restriction
205		if is_symbolic:
206			resource_path = self.UI_PATH + 'toolbar-symbolic.ui'
207		else:
208			resource_path = self.UI_PATH + 'toolbar.ui'
209		builder = Gtk.Builder.new_from_resource(resource_path)
210
211		# Composition over inheritance
212		self._widget = builder.get_object('toolbar')
213		window.toolbar_box.pack_start(self._widget, True, True, 0)
214		window.toolbar_box.show_all()
215
216		# Mandatory widget name, used for the `win.main_menu` action
217		self._main_menu_btn = builder.get_object('main_menu_btn')
218
219		# History buttons whose tooltips depends on the last operation
220		# XXX maybe later
221		# self._undo_btn = builder.get_object('undo_btn')
222		# self._redo_btn = builder.get_object('redo_btn')
223
224		# The toolbar has menus which need to be set manually
225		builder.add_from_resource(self.UI_PATH + 'win-menus.ui')
226
227		new_btn = builder.get_object('new_menu_btn')
228		new_menu = Gtk.Menu.new_from_model(builder.get_object('new-image-menu'))
229		new_btn.set_menu(new_menu)
230
231		save_btn = builder.get_object('save_menu_btn')
232		save_menu = Gtk.Menu.new_from_model(builder.get_object('save-section'))
233		save_btn.set_menu(save_menu)
234
235		if with_menubar:
236			self._main_menu_btn.set_visible(False)
237		else:
238			others_menu = builder.get_object('minimal-window-menu')
239			self._main_menu_btn.set_menu_model(others_menu)
240
241	def remove_from_ui(self):
242		self._widget.destroy()
243		return False
244
245	# def set_undo_label(self, label):
246	#	super().set_undo_label(label)
247	# 	if label is None:
248	# 		self._undo_btn.set_tooltip_text(_("Undo"))
249	# 	else:
250	# 		self._undo_btn.set_tooltip_text(_("Undo %s") % label)
251
252	# def set_redo_label(self, label):
253	#	super().set_redo_label(label)
254	# 	if label is None:
255	# 		self._redo_btn.set_tooltip_text(_("Redo"))
256	# 	else:
257	# 		self._redo_btn.set_tooltip_text(_("Redo %s") % label)
258
259	############################################################################
260################################################################################
261
262