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