Daniel Brötzmann pushed to branch master at gajim / gajim
Commits:
-
5581a800
by wurstsalat at 2021-12-21T19:20:05+01:00
-
2a337add
by wurstsalat at 2021-12-21T19:20:05+01:00
2 changed files:
Changes:
| ... | ... | @@ -15,8 +15,14 @@ |
| 15 | 15 |
# You should have received a copy of the GNU General Public License
|
| 16 | 16 |
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
| 17 | 17 |
|
| 18 |
+from __future__ import annotations
|
|
| 19 |
+from typing import Optional
|
|
| 20 |
+from typing import Union
|
|
| 21 |
+from typing import Tuple
|
|
| 22 |
+ |
|
| 18 | 23 |
import math
|
| 19 | 24 |
import logging
|
| 25 |
+from pathlib import Path
|
|
| 20 | 26 |
|
| 21 | 27 |
from gi.repository import Gtk
|
| 22 | 28 |
from gi.repository import Gdk
|
| ... | ... | @@ -33,8 +39,8 @@ |
| 33 | 39 |
settings = Gtk.Settings.get_default()
|
| 34 | 40 |
|
| 35 | 41 |
|
| 36 |
-class CSSConfig():
|
|
| 37 |
- def __init__(self):
|
|
| 42 |
+class CSSConfig:
|
|
| 43 |
+ def __init__(self) -> None:
|
|
| 38 | 44 |
"""
|
| 39 | 45 |
CSSConfig handles loading and storing of all relevant Gajim style files
|
| 40 | 46 |
|
| ... | ... | @@ -69,24 +75,24 @@ def __init__(self): |
| 69 | 75 |
css_parser.ser.prefs.keepEmptyRules = False
|
| 70 | 76 |
|
| 71 | 77 |
# Holds the currently selected theme in the Theme Editor
|
| 72 |
- self._pre_css = None
|
|
| 73 |
- self._pre_css_path = None
|
|
| 78 |
+ self._pre_css: Optional[css_parser.css.CSSStyleSheet] = None
|
|
| 79 |
+ self._pre_css_path: Optional[Path] = None
|
|
| 74 | 80 |
|
| 75 | 81 |
# Holds the default theme, its used if values are not found
|
| 76 | 82 |
# in the selected theme
|
| 77 |
- self._default_css = None
|
|
| 78 |
- self._default_css_path = None
|
|
| 83 |
+ self._default_css: Optional[css_parser.css.CSSStyleSheet] = None
|
|
| 84 |
+ self._default_css_path: Optional[Path] = None
|
|
| 79 | 85 |
|
| 80 | 86 |
# Holds the currently selected theme
|
| 81 |
- self._css = None
|
|
| 82 |
- self._css_path = None
|
|
| 87 |
+ self._css: Optional[css_parser.css.CSSStyleSheet] = None
|
|
| 88 |
+ self._css_path: Optional[Path] = None
|
|
| 83 | 89 |
|
| 84 | 90 |
# User Theme CSS Provider
|
| 85 | 91 |
self._provider = Gtk.CssProvider()
|
| 86 | 92 |
|
| 87 | 93 |
# Used for dynamic classes like account colors
|
| 88 | 94 |
self._dynamic_provider = Gtk.CssProvider()
|
| 89 |
- self._dynamic_dict = {}
|
|
| 95 |
+ self._dynamic_dict: dict[str, str] = {}
|
|
| 90 | 96 |
self.refresh()
|
| 91 | 97 |
|
| 92 | 98 |
Gtk.StyleContext.add_provider_for_screen(
|
| ... | ... | @@ -95,10 +101,10 @@ def __init__(self): |
| 95 | 101 |
CSSPriority.APPLICATION)
|
| 96 | 102 |
|
| 97 | 103 |
# Cache of recently requested values
|
| 98 |
- self._cache = {}
|
|
| 104 |
+ self._cache: dict[str, Union[str, Pango.FontDescription, None]] = {}
|
|
| 99 | 105 |
|
| 100 | 106 |
# Holds all currently available themes
|
| 101 |
- self.themes = []
|
|
| 107 |
+ self.themes: list[str] = []
|
|
| 102 | 108 |
|
| 103 | 109 |
self.set_dark_theme()
|
| 104 | 110 |
self._load_css()
|
| ... | ... | @@ -113,7 +119,7 @@ def __init__(self): |
| 113 | 119 |
CSSPriority.USER_THEME)
|
| 114 | 120 |
|
| 115 | 121 |
@property
|
| 116 |
- def prefer_dark(self):
|
|
| 122 |
+ def prefer_dark(self) -> bool:
|
|
| 117 | 123 |
setting = app.settings.get('dark_theme')
|
| 118 | 124 |
if setting == Theme.SYSTEM:
|
| 119 | 125 |
if settings is None:
|
| ... | ... | @@ -121,7 +127,7 @@ def prefer_dark(self): |
| 121 | 127 |
return settings.get_property('gtk-application-prefer-dark-theme')
|
| 122 | 128 |
return setting == Theme.DARK
|
| 123 | 129 |
|
| 124 |
- def set_dark_theme(self, value=None):
|
|
| 130 |
+ def set_dark_theme(self, value: Optional[int] = None) -> None:
|
|
| 125 | 131 |
if value is None:
|
| 126 | 132 |
value = app.settings.get('dark_theme')
|
| 127 | 133 |
else:
|
| ... | ... | @@ -135,7 +141,7 @@ def set_dark_theme(self, value=None): |
| 135 | 141 |
settings.set_property('gtk-application-prefer-dark-theme', bool(value))
|
| 136 | 142 |
self._load_css()
|
| 137 | 143 |
|
| 138 |
- def _load_css(self):
|
|
| 144 |
+ def _load_css(self) -> None:
|
|
| 139 | 145 |
self._load_css_from_file('gajim.css', CSSPriority.APPLICATION)
|
| 140 | 146 |
if self.prefer_dark:
|
| 141 | 147 |
self._load_css_from_file('gajim-dark.css',
|
| ... | ... | @@ -146,7 +152,10 @@ def _load_css(self): |
| 146 | 152 |
self._load_css_from_file('default-dark.css',
|
| 147 | 153 |
CSSPriority.DEFAULT_THEME_DARK)
|
| 148 | 154 |
|
| 149 |
- def _load_css_from_file(self, filename, priority):
|
|
| 155 |
+ def _load_css_from_file(self,
|
|
| 156 |
+ filename: str,
|
|
| 157 |
+ priority: CSSPriority
|
|
| 158 |
+ ) -> None:
|
|
| 150 | 159 |
path = configpaths.get('STYLE') / filename
|
| 151 | 160 |
try:
|
| 152 | 161 |
with open(path, "r", encoding='utf8') as file_:
|
| ... | ... | @@ -156,7 +165,7 @@ def _load_css_from_file(self, filename, priority): |
| 156 | 165 |
return
|
| 157 | 166 |
self._activate_css(css, priority)
|
| 158 | 167 |
|
| 159 |
- def _activate_css(self, css, priority):
|
|
| 168 |
+ def _activate_css(self, css: str, priority: CSSPriority) -> None:
|
|
| 160 | 169 |
try:
|
| 161 | 170 |
provider = Gtk.CssProvider()
|
| 162 | 171 |
provider.load_from_data(bytes(css.encode('utf-8')))
|
| ... | ... | @@ -170,7 +179,7 @@ def _activate_css(self, css, priority): |
| 170 | 179 |
log.exception('Error loading application css')
|
| 171 | 180 |
|
| 172 | 181 |
@staticmethod
|
| 173 |
- def _pango_to_css_weight(number):
|
|
| 182 |
+ def _pango_to_css_weight(number: str) -> int:
|
|
| 174 | 183 |
# Pango allows for weight values between 100 and 1000
|
| 175 | 184 |
# CSS allows only full hundred numbers like 100, 200 ..
|
| 176 | 185 |
number = int(number)
|
| ... | ... | @@ -180,14 +189,14 @@ def _pango_to_css_weight(number): |
| 180 | 189 |
return 900
|
| 181 | 190 |
return int(math.ceil(number / 100.0)) * 100
|
| 182 | 191 |
|
| 183 |
- def _gather_available_themes(self):
|
|
| 192 |
+ def _gather_available_themes(self) -> None:
|
|
| 184 | 193 |
files = configpaths.get('MY_THEME').iterdir()
|
| 185 | 194 |
self.themes = [file.stem for file in files if file.suffix == '.css']
|
| 186 | 195 |
if 'default' in self.themes:
|
| 187 | 196 |
# Ignore user created themes that are named 'default'
|
| 188 | 197 |
self.themes.remove('default')
|
| 189 | 198 |
|
| 190 |
- def get_theme_path(self, theme, user=True):
|
|
| 199 |
+ def get_theme_path(self, theme: str, user: bool = True) -> Path:
|
|
| 191 | 200 |
if theme == 'default' and self.prefer_dark:
|
| 192 | 201 |
theme = 'default-dark'
|
| 193 | 202 |
|
| ... | ... | @@ -195,7 +204,7 @@ def get_theme_path(self, theme, user=True): |
| 195 | 204 |
return configpaths.get('MY_THEME') / f'{theme}.css'
|
| 196 | 205 |
return configpaths.get('STYLE') / f'{theme}.css'
|
| 197 | 206 |
|
| 198 |
- def _determine_theme_path(self):
|
|
| 207 |
+ def _determine_theme_path(self) -> Path:
|
|
| 199 | 208 |
# Gets the path of the currently active theme.
|
| 200 | 209 |
# If it does not exist, it falls back to the default theme
|
| 201 | 210 |
theme = app.settings.get('roster_theme')
|
| ... | ... | @@ -211,23 +220,23 @@ def _determine_theme_path(self): |
| 211 | 220 |
log.info('Use Theme: %s', theme)
|
| 212 | 221 |
return theme_path
|
| 213 | 222 |
|
| 214 |
- def _load_selected(self, new_path=None):
|
|
| 223 |
+ def _load_selected(self, new_path: Optional[Path] = None) -> None:
|
|
| 215 | 224 |
if new_path is None:
|
| 216 | 225 |
self._css_path = self._determine_theme_path()
|
| 217 | 226 |
else:
|
| 218 | 227 |
self._css_path = new_path
|
| 219 | 228 |
self._css = css_parser.parseFile(self._css_path)
|
| 220 | 229 |
|
| 221 |
- def _load_default(self):
|
|
| 230 |
+ def _load_default(self) -> None:
|
|
| 222 | 231 |
self._default_css_path = self.get_theme_path('default', user=False)
|
| 223 | 232 |
self._default_css = css_parser.parseFile(self._default_css_path)
|
| 224 | 233 |
|
| 225 |
- def _load_pre(self, theme):
|
|
| 234 |
+ def _load_pre(self, theme: str) -> None:
|
|
| 226 | 235 |
log.info('Preload theme %s', theme)
|
| 227 | 236 |
self._pre_css_path = self.get_theme_path(theme)
|
| 228 | 237 |
self._pre_css = css_parser.parseFile(self._pre_css_path)
|
| 229 | 238 |
|
| 230 |
- def _write(self, pre):
|
|
| 239 |
+ def _write(self, pre: bool) -> None:
|
|
| 231 | 240 |
path = self._css_path
|
| 232 | 241 |
css = self._css
|
| 233 | 242 |
if pre:
|
| ... | ... | @@ -241,10 +250,16 @@ def _write(self, pre): |
| 241 | 250 |
self._load_selected()
|
| 242 | 251 |
self._activate_theme()
|
| 243 | 252 |
|
| 244 |
- def set_value(self, selector, attr, value, pre=False):
|
|
| 253 |
+ def set_value(self,
|
|
| 254 |
+ selector: str,
|
|
| 255 |
+ attr: StyleAttr,
|
|
| 256 |
+ value: Union[str, Pango.FontDescription],
|
|
| 257 |
+ pre: bool = False
|
|
| 258 |
+ ) -> None:
|
|
| 245 | 259 |
if attr == StyleAttr.FONT:
|
| 246 | 260 |
# forward to set_font() for convenience
|
| 247 |
- return self.set_font(selector, value, pre)
|
|
| 261 |
+ self.set_font(selector, value, pre)
|
|
| 262 |
+ return
|
|
| 248 | 263 |
|
| 249 | 264 |
if isinstance(attr, StyleAttr):
|
| 250 | 265 |
attr = attr.value
|
| ... | ... | @@ -261,7 +276,7 @@ def set_value(self, selector, attr, value, pre=False): |
| 261 | 276 |
if not pre:
|
| 262 | 277 |
self._add_to_cache(selector, attr, value)
|
| 263 | 278 |
self._write(pre)
|
| 264 |
- return None
|
|
| 279 |
+ return
|
|
| 265 | 280 |
|
| 266 | 281 |
# The rule was not found, so we add it to this theme
|
| 267 | 282 |
log.info('Set %s %s %s', selector, attr, value)
|
| ... | ... | @@ -269,9 +284,12 @@ def set_value(self, selector, attr, value, pre=False): |
| 269 | 284 |
rule.style[attr] = value
|
| 270 | 285 |
css.add(rule)
|
| 271 | 286 |
self._write(pre)
|
| 272 |
- return None
|
|
| 273 | 287 |
|
| 274 |
- def set_font(self, selector, description, pre=False):
|
|
| 288 |
+ def set_font(self,
|
|
| 289 |
+ selector: str,
|
|
| 290 |
+ description: Pango.FontDescription,
|
|
| 291 |
+ pre: bool = False
|
|
| 292 |
+ ) -> None:
|
|
| 275 | 293 |
css = self._css
|
| 276 | 294 |
if pre:
|
| 277 | 295 |
css = self._pre_css
|
| ... | ... | @@ -285,7 +303,7 @@ def set_font(self, selector, description, pre=False): |
| 285 | 303 |
selector, family, size, style, weight)
|
| 286 | 304 |
rule.style['font-family'] = family
|
| 287 | 305 |
rule.style['font-style'] = style
|
| 288 |
- rule.style['font-size'] = '%spt' % size
|
|
| 306 |
+ rule.style['font-size'] = f'{size}pt'
|
|
| 289 | 307 |
rule.style['font-weight'] = weight
|
| 290 | 308 |
|
| 291 | 309 |
if not pre:
|
| ... | ... | @@ -300,19 +318,24 @@ def set_font(self, selector, description, pre=False): |
| 300 | 318 |
rule = css_parser.css.CSSStyleRule(selectorText=selector)
|
| 301 | 319 |
rule.style['font-family'] = family
|
| 302 | 320 |
rule.style['font-style'] = style
|
| 303 |
- rule.style['font-size'] = '%spt' % size
|
|
| 321 |
+ rule.style['font-size'] = f'{size}pt'
|
|
| 304 | 322 |
rule.style['font-weight'] = weight
|
| 305 | 323 |
css.add(rule)
|
| 306 | 324 |
self._write(pre)
|
| 307 | 325 |
|
| 308 |
- def _get_attr_from_description(self, description):
|
|
| 326 |
+ def _get_attr_from_description(self,
|
|
| 327 |
+ description: Pango.FontDescription
|
|
| 328 |
+ ) -> Tuple[Optional[str], int, str, int]:
|
|
| 309 | 329 |
size = description.get_size() / Pango.SCALE
|
| 310 | 330 |
style = self._get_string_from_pango_style(description.get_style())
|
| 311 |
- weight = self._pango_to_css_weight(int(description.get_weight()))
|
|
| 331 |
+ weight = self._pango_to_css_weight(description.get_weight())
|
|
| 312 | 332 |
family = description.get_family()
|
| 313 | 333 |
return family, size, style, weight
|
| 314 | 334 |
|
| 315 |
- def _get_default_rule(self, selector, _attr):
|
|
| 335 |
+ def _get_default_rule(self,
|
|
| 336 |
+ selector: str,
|
|
| 337 |
+ _attr: StyleAttr
|
|
| 338 |
+ ) -> Optional[css_parser.css.CSSStyleRule]:
|
|
| 316 | 339 |
for rule in self._default_css:
|
| 317 | 340 |
if rule.type != rule.STYLE_RULE:
|
| 318 | 341 |
continue
|
| ... | ... | @@ -321,7 +344,10 @@ def _get_default_rule(self, selector, _attr): |
| 321 | 344 |
return rule
|
| 322 | 345 |
return None
|
| 323 | 346 |
|
| 324 |
- def get_font(self, selector, pre=False):
|
|
| 347 |
+ def get_font(self,
|
|
| 348 |
+ selector: str,
|
|
| 349 |
+ pre: bool = False
|
|
| 350 |
+ ) -> Optional[Pango.FontDescription]:
|
|
| 325 | 351 |
if pre:
|
| 326 | 352 |
css = self._pre_css
|
| 327 | 353 |
else:
|
| ... | ... | @@ -353,7 +379,12 @@ def get_font(self, selector, pre=False): |
| 353 | 379 |
self._add_to_cache(selector, 'fontdescription', None)
|
| 354 | 380 |
return None
|
| 355 | 381 |
|
| 356 |
- def _get_description_from_css(self, family, size, style, weight):
|
|
| 382 |
+ def _get_description_from_css(self,
|
|
| 383 |
+ family: Optional[str],
|
|
| 384 |
+ size: Optional[str],
|
|
| 385 |
+ style: Optional[str],
|
|
| 386 |
+ weight: Optional[str]
|
|
| 387 |
+ ) -> Optional[Pango.FontDescription]:
|
|
| 357 | 388 |
if family is None:
|
| 358 | 389 |
return None
|
| 359 | 390 |
desc = Pango.FontDescription()
|
| ... | ... | @@ -367,7 +398,7 @@ def _get_description_from_css(self, family, size, style, weight): |
| 367 | 398 |
return desc
|
| 368 | 399 |
|
| 369 | 400 |
@staticmethod
|
| 370 |
- def _get_pango_style_from_string(style: str) -> int:
|
|
| 401 |
+ def _get_pango_style_from_string(style: str) -> Pango.Style:
|
|
| 371 | 402 |
if style == 'normal':
|
| 372 | 403 |
return Pango.Style(0)
|
| 373 | 404 |
if style == 'oblique':
|
| ... | ... | @@ -384,7 +415,11 @@ def _get_string_from_pango_style(style: Pango.Style) -> str: |
| 384 | 415 |
# Pango.Style.OBLIQUE:
|
| 385 | 416 |
return 'oblique'
|
| 386 | 417 |
|
| 387 |
- def get_value(self, selector, attr, pre=False):
|
|
| 418 |
+ def get_value(self,
|
|
| 419 |
+ selector: str,
|
|
| 420 |
+ attr: StyleAttr,
|
|
| 421 |
+ pre: bool = False
|
|
| 422 |
+ ) -> Union[str, Pango.FontDescription, None]:
|
|
| 388 | 423 |
if attr == StyleAttr.FONT:
|
| 389 | 424 |
# forward to get_font() for convenience
|
| 390 | 425 |
return self.get_font(selector, pre)
|
| ... | ... | @@ -422,10 +457,15 @@ def get_value(self, selector, attr, pre=False): |
| 422 | 457 |
return value
|
| 423 | 458 |
return None
|
| 424 | 459 |
|
| 425 |
- def remove_value(self, selector, attr, pre=False):
|
|
| 460 |
+ def remove_value(self,
|
|
| 461 |
+ selector: str,
|
|
| 462 |
+ attr: StyleAttr,
|
|
| 463 |
+ pre: bool = False
|
|
| 464 |
+ ) -> None:
|
|
| 426 | 465 |
if attr == StyleAttr.FONT:
|
| 427 | 466 |
# forward to remove_font() for convenience
|
| 428 |
- return self.remove_font(selector, pre)
|
|
| 467 |
+ self.remove_font(selector, pre)
|
|
| 468 |
+ return
|
|
| 429 | 469 |
|
| 430 | 470 |
if isinstance(attr, StyleAttr):
|
| 431 | 471 |
attr = attr.value
|
| ... | ... | @@ -441,9 +481,8 @@ def remove_value(self, selector, attr, pre=False): |
| 441 | 481 |
rule.style.removeProperty(attr)
|
| 442 | 482 |
break
|
| 443 | 483 |
self._write(pre)
|
| 444 |
- return None
|
|
| 445 | 484 |
|
| 446 |
- def remove_font(self, selector, pre=False):
|
|
| 485 |
+ def remove_font(self, selector: str, pre: bool = False) -> None:
|
|
| 447 | 486 |
css = self._css
|
| 448 | 487 |
if pre:
|
| 449 | 488 |
css = self._pre_css
|
| ... | ... | @@ -460,7 +499,7 @@ def remove_font(self, selector, pre=False): |
| 460 | 499 |
break
|
| 461 | 500 |
self._write(pre)
|
| 462 | 501 |
|
| 463 |
- def change_theme(self, theme):
|
|
| 502 |
+ def change_theme(self, theme: str) -> bool:
|
|
| 464 | 503 |
user = not theme == 'default'
|
| 465 | 504 |
theme_path = self.get_theme_path(theme, user=user)
|
| 466 | 505 |
if not theme_path.exists():
|
| ... | ... | @@ -472,7 +511,7 @@ def change_theme(self, theme): |
| 472 | 511 |
log.info('Change Theme: Successful switched to %s', theme)
|
| 473 | 512 |
return True
|
| 474 | 513 |
|
| 475 |
- def change_preload_theme(self, theme):
|
|
| 514 |
+ def change_preload_theme(self, theme: str) -> bool:
|
|
| 476 | 515 |
theme_path = self.get_theme_path(theme)
|
| 477 | 516 |
if not theme_path.exists():
|
| 478 | 517 |
log.error('Change Preload Theme: Theme %s does not exist',
|
| ... | ... | @@ -482,7 +521,7 @@ def change_preload_theme(self, theme): |
| 482 | 521 |
log.info('Successful switched to %s', theme)
|
| 483 | 522 |
return True
|
| 484 | 523 |
|
| 485 |
- def rename_theme(self, old_theme, new_theme):
|
|
| 524 |
+ def rename_theme(self, old_theme: str, new_theme: str) -> bool:
|
|
| 486 | 525 |
if old_theme not in self.themes:
|
| 487 | 526 |
log.error('Rename Theme: Old theme %s not found', old_theme)
|
| 488 | 527 |
return False
|
| ... | ... | @@ -501,12 +540,12 @@ def rename_theme(self, old_theme, new_theme): |
| 501 | 540 |
old_theme, new_theme)
|
| 502 | 541 |
return True
|
| 503 | 542 |
|
| 504 |
- def _activate_theme(self):
|
|
| 543 |
+ def _activate_theme(self) -> None:
|
|
| 505 | 544 |
log.info('Activate theme')
|
| 506 | 545 |
self._invalidate_cache()
|
| 507 | 546 |
self._provider.load_from_data(self._css.cssText)
|
| 508 | 547 |
|
| 509 |
- def add_new_theme(self, theme):
|
|
| 548 |
+ def add_new_theme(self, theme: str) -> bool:
|
|
| 510 | 549 |
theme_path = self.get_theme_path(theme)
|
| 511 | 550 |
if theme_path.exists():
|
| 512 | 551 |
log.error('Add Theme: %s exists already', theme_path)
|
| ... | ... | @@ -517,32 +556,39 @@ def add_new_theme(self, theme): |
| 517 | 556 |
log.info('Add Theme: Successful added theme %s', theme)
|
| 518 | 557 |
return True
|
| 519 | 558 |
|
| 520 |
- def remove_theme(self, theme):
|
|
| 559 |
+ def remove_theme(self, theme: str) -> None:
|
|
| 521 | 560 |
theme_path = self.get_theme_path(theme)
|
| 522 | 561 |
if theme_path.exists():
|
| 523 | 562 |
theme_path.unlink()
|
| 524 | 563 |
self.themes.remove(theme)
|
| 525 | 564 |
log.info('Remove Theme: Successful removed theme %s', theme)
|
| 526 | 565 |
|
| 527 |
- def _add_to_cache(self, selector, attr, value):
|
|
| 566 |
+ def _add_to_cache(self,
|
|
| 567 |
+ selector: str,
|
|
| 568 |
+ attr: StyleAttr,
|
|
| 569 |
+ value: Union[str, Pango.FontDescription, None]
|
|
| 570 |
+ ) -> None:
|
|
| 528 | 571 |
self._cache[selector + attr] = value
|
| 529 | 572 |
|
| 530 |
- def _get_from_cache(self, selector, attr):
|
|
| 573 |
+ def _get_from_cache(self,
|
|
| 574 |
+ selector: str,
|
|
| 575 |
+ attr: StyleAttr
|
|
| 576 |
+ ) -> Union[str, Pango.FontDescription, None]:
|
|
| 531 | 577 |
return self._cache[selector + attr]
|
| 532 | 578 |
|
| 533 |
- def _invalidate_cache(self):
|
|
| 579 |
+ def _invalidate_cache(self) -> None:
|
|
| 534 | 580 |
self._cache = {}
|
| 535 | 581 |
|
| 536 |
- def refresh(self):
|
|
| 582 |
+ def refresh(self) -> None:
|
|
| 537 | 583 |
css = ''
|
| 538 | 584 |
accounts = app.settings.get_accounts()
|
| 539 | 585 |
for index, account in enumerate(accounts):
|
| 540 | 586 |
color = app.settings.get_account_setting(account, 'account_color')
|
| 541 |
- css_class = 'gajim_class_%s' % index
|
|
| 587 |
+ css_class = f'gajim_class_{index}'
|
|
| 542 | 588 |
css += '.%s { background-color: %s }\n' % (css_class, color)
|
| 543 | 589 |
self._dynamic_dict[account] = css_class
|
| 544 | 590 |
|
| 545 | 591 |
self._dynamic_provider.load_from_data(css.encode())
|
| 546 | 592 |
|
| 547 |
- def get_dynamic_class(self, name):
|
|
| 593 |
+ def get_dynamic_class(self, name: str) -> str:
|
|
| 548 | 594 |
return self._dynamic_dict[name]
|
| ... | ... | @@ -14,11 +14,16 @@ |
| 14 | 14 |
# You should have received a copy of the GNU General Public License
|
| 15 | 15 |
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
| 16 | 16 |
|
| 17 |
-from collections import namedtuple
|
|
| 17 |
+from __future__ import annotations
|
|
| 18 |
+from typing import NamedTuple
|
|
| 19 |
+from typing import Any
|
|
| 20 |
+from typing import Union
|
|
| 21 |
+ |
|
| 18 | 22 |
from enum import IntEnum
|
| 19 | 23 |
|
| 20 | 24 |
from gi.repository import Gtk
|
| 21 | 25 |
from gi.repository import Gdk
|
| 26 |
+from gi.repository import Pango
|
|
| 22 | 27 |
|
| 23 | 28 |
from gajim.common import app
|
| 24 | 29 |
from gajim.common.nec import NetworkEvent
|
| ... | ... | @@ -31,9 +36,14 @@ |
| 31 | 36 |
from .builder import get_builder
|
| 32 | 37 |
from .util import get_app_window
|
| 33 | 38 |
|
| 34 |
-StyleOption = namedtuple('StyleOption', 'label selector attr')
|
|
| 35 | 39 |
|
| 36 |
-CSS_STYLE_OPTIONS = [
|
|
| 40 |
+class StyleOption(NamedTuple):
|
|
| 41 |
+ label: str
|
|
| 42 |
+ selector: str
|
|
| 43 |
+ attr: StyleAttr
|
|
| 44 |
+ |
|
| 45 |
+ |
|
| 46 |
+CSS_STYLE_OPTIONS: list[StyleOption] = [
|
|
| 37 | 47 |
StyleOption(_('Conversation: Text Font'),
|
| 38 | 48 |
'.gajim-conversation-text',
|
| 39 | 49 |
StyleAttr.FONT),
|
| ... | ... | @@ -129,7 +139,7 @@ class Column(IntEnum): |
| 129 | 139 |
|
| 130 | 140 |
|
| 131 | 141 |
class Themes(Gtk.ApplicationWindow):
|
| 132 |
- def __init__(self, transient):
|
|
| 142 |
+ def __init__(self, transient: Gtk.Window) -> None:
|
|
| 133 | 143 |
Gtk.ApplicationWindow.__init__(self)
|
| 134 | 144 |
self.set_application(app.app)
|
| 135 | 145 |
self.set_title(_('Gajim Themes'))
|
| ... | ... | @@ -154,11 +164,14 @@ def __init__(self, transient): |
| 154 | 164 |
|
| 155 | 165 |
self._fill_choose_listbox()
|
| 156 | 166 |
|
| 157 |
- def _on_key_press(self, _widget, event):
|
|
| 167 |
+ def _on_key_press(self,
|
|
| 168 |
+ _widget: Gtk.Widget,
|
|
| 169 |
+ event: Gdk.EventKey
|
|
| 170 |
+ ) -> None:
|
|
| 158 | 171 |
if event.keyval == Gdk.KEY_Escape:
|
| 159 | 172 |
self.destroy()
|
| 160 | 173 |
|
| 161 |
- def _get_themes(self):
|
|
| 174 |
+ def _get_themes(self) -> None:
|
|
| 162 | 175 |
current_theme = app.settings.get('roster_theme')
|
| 163 | 176 |
for theme in app.css_config.themes:
|
| 164 | 177 |
if theme == current_theme:
|
| ... | ... | @@ -166,7 +179,10 @@ def _get_themes(self): |
| 166 | 179 |
continue
|
| 167 | 180 |
self._ui.theme_store.append([theme])
|
| 168 | 181 |
|
| 169 |
- def _on_theme_name_edit(self, _renderer, path, new_name):
|
|
| 182 |
+ def _on_theme_name_edit(self,
|
|
| 183 |
+ _renderer: Gtk.CellRendererText,
|
|
| 184 |
+ path: str,
|
|
| 185 |
+ new_name: str) -> None:
|
|
| 170 | 186 |
iter_ = self._ui.theme_store.get_iter(path)
|
| 171 | 187 |
old_name = self._ui.theme_store[iter_][Column.THEME]
|
| 172 | 188 |
|
| ... | ... | @@ -194,10 +210,10 @@ def _on_theme_name_edit(self, _renderer, path, new_name): |
| 194 | 210 |
app.settings.set('roster_theme', new_name)
|
| 195 | 211 |
self._ui.theme_store.set_value(iter_, Column.THEME, new_name)
|
| 196 | 212 |
|
| 197 |
- def _select_theme_row(self, iter_):
|
|
| 213 |
+ def _select_theme_row(self, iter_: Gtk.TreeIter) -> None:
|
|
| 198 | 214 |
self._ui.theme_treeview.get_selection().select_iter(iter_)
|
| 199 | 215 |
|
| 200 |
- def _on_theme_selected(self, tree_selection):
|
|
| 216 |
+ def _on_theme_selected(self, tree_selection: Gtk.TreeSelection) -> None:
|
|
| 201 | 217 |
store, iter_ = tree_selection.get_selected()
|
| 202 | 218 |
if iter_ is None:
|
| 203 | 219 |
self._clear_options()
|
| ... | ... | @@ -210,7 +226,7 @@ def _on_theme_selected(self, tree_selection): |
| 210 | 226 |
self._apply_theme(theme)
|
| 211 | 227 |
app.nec.push_incoming_event(NetworkEvent('style-changed'))
|
| 212 | 228 |
|
| 213 |
- def _load_options(self):
|
|
| 229 |
+ def _load_options(self) -> None:
|
|
| 214 | 230 |
self._ui.option_listbox.foreach(self._remove_option)
|
| 215 | 231 |
for option in CSS_STYLE_OPTIONS:
|
| 216 | 232 |
value = app.css_config.get_value(
|
| ... | ... | @@ -222,7 +238,7 @@ def _load_options(self): |
| 222 | 238 |
row = Option(option, value)
|
| 223 | 239 |
self._ui.option_listbox.add(row)
|
| 224 | 240 |
|
| 225 |
- def _add_option(self, _listbox, row):
|
|
| 241 |
+ def _add_option(self, _listbox: Gtk.ListBox, row: Gtk.ListBoxRow) -> None:
|
|
| 226 | 242 |
# Add theme if there is none
|
| 227 | 243 |
store, _ = self._ui.theme_treeview.get_selection().get_selected()
|
| 228 | 244 |
first = store.get_iter_first()
|
| ... | ... | @@ -242,18 +258,18 @@ def _add_option(self, _listbox, row): |
| 242 | 258 |
self._ui.option_listbox.add(row)
|
| 243 | 259 |
self._ui.option_popover.popdown()
|
| 244 | 260 |
|
| 245 |
- def _clear_options(self):
|
|
| 261 |
+ def _clear_options(self) -> None:
|
|
| 246 | 262 |
self._ui.option_listbox.foreach(self._remove_option)
|
| 247 | 263 |
|
| 248 |
- def _fill_choose_listbox(self):
|
|
| 264 |
+ def _fill_choose_listbox(self) -> None:
|
|
| 249 | 265 |
for option in CSS_STYLE_OPTIONS:
|
| 250 | 266 |
self._ui.choose_option_listbox.add(ChooseOption(option))
|
| 251 | 267 |
|
| 252 |
- def _remove_option(self, row):
|
|
| 268 |
+ def _remove_option(self, row: Gtk.ListBoxRow) -> None:
|
|
| 253 | 269 |
self._ui.option_listbox.remove(row)
|
| 254 | 270 |
row.destroy()
|
| 255 | 271 |
|
| 256 |
- def _on_add_new_theme(self, *args):
|
|
| 272 |
+ def _on_add_new_theme(self, *args: Any) -> None:
|
|
| 257 | 273 |
name = self._create_theme_name()
|
| 258 | 274 |
if not app.css_config.add_new_theme(name):
|
| 259 | 275 |
return
|
| ... | ... | @@ -265,7 +281,7 @@ def _on_add_new_theme(self, *args): |
| 265 | 281 |
self._select_theme_row(iter_)
|
| 266 | 282 |
self._apply_theme(name)
|
| 267 | 283 |
|
| 268 |
- def reload_roster_theme(self):
|
|
| 284 |
+ def reload_roster_theme(self) -> None:
|
|
| 269 | 285 |
tree_selection = self._ui.theme_treeview.get_selection()
|
| 270 | 286 |
store, iter_ = tree_selection.get_selected()
|
| 271 | 287 |
if iter_ is None:
|
| ... | ... | @@ -274,25 +290,25 @@ def reload_roster_theme(self): |
| 274 | 290 |
self._apply_theme(theme)
|
| 275 | 291 |
|
| 276 | 292 |
@staticmethod
|
| 277 |
- def _apply_theme(theme):
|
|
| 293 |
+ def _apply_theme(theme: str) -> None:
|
|
| 278 | 294 |
app.settings.set('roster_theme', theme)
|
| 279 | 295 |
app.css_config.change_theme(theme)
|
| 280 | 296 |
app.nec.push_incoming_event(NetworkEvent('theme-update'))
|
| 281 | 297 |
|
| 282 | 298 |
@staticmethod
|
| 283 |
- def _update_preferences_window():
|
|
| 299 |
+ def _update_preferences_window() -> None:
|
|
| 284 | 300 |
window = get_app_window('Preferences')
|
| 285 | 301 |
if window is not None:
|
| 286 | 302 |
window.update_theme_list()
|
| 287 | 303 |
|
| 288 | 304 |
@staticmethod
|
| 289 |
- def _create_theme_name():
|
|
| 290 |
- i = 0
|
|
| 291 |
- while 'newtheme%s' % i in app.css_config.themes:
|
|
| 292 |
- i += 1
|
|
| 293 |
- return 'newtheme%s' % i
|
|
| 305 |
+ def _create_theme_name() -> str:
|
|
| 306 |
+ index = 0
|
|
| 307 |
+ while f'newtheme{index}' in app.css_config.themes:
|
|
| 308 |
+ index += 1
|
|
| 309 |
+ return f'newtheme{index}'
|
|
| 294 | 310 |
|
| 295 |
- def _on_remove_theme(self, *args):
|
|
| 311 |
+ def _on_remove_theme(self, *args: Any) -> None:
|
|
| 296 | 312 |
store, iter_ = self._ui.theme_treeview.get_selection().get_selected()
|
| 297 | 313 |
if iter_ is None:
|
| 298 | 314 |
return
|
| ... | ... | @@ -329,7 +345,10 @@ def _remove_theme(): |
| 329 | 345 |
|
| 330 | 346 |
|
| 331 | 347 |
class Option(Gtk.ListBoxRow):
|
| 332 |
- def __init__(self, option, value):
|
|
| 348 |
+ def __init__(self,
|
|
| 349 |
+ option: StyleOption,
|
|
| 350 |
+ value: Union[str, Pango.FontDescription, None]
|
|
| 351 |
+ ) -> None:
|
|
| 333 | 352 |
Gtk.ListBoxRow.__init__(self)
|
| 334 | 353 |
self.option = option
|
| 335 | 354 |
self._box = Gtk.Box(spacing=12)
|
| ... | ... | @@ -355,7 +374,7 @@ def __init__(self, option, value): |
| 355 | 374 |
self.add(self._box)
|
| 356 | 375 |
self.show_all()
|
| 357 | 376 |
|
| 358 |
- def _init_color(self, color):
|
|
| 377 |
+ def _init_color(self, color: str) -> None:
|
|
| 359 | 378 |
color_button = Gtk.ColorButton()
|
| 360 | 379 |
if color is not None:
|
| 361 | 380 |
rgba = Gdk.RGBA()
|
| ... | ... | @@ -365,7 +384,7 @@ def _init_color(self, color): |
| 365 | 384 |
color_button.connect('color-set', self._on_color_set)
|
| 366 | 385 |
self._box.add(color_button)
|
| 367 | 386 |
|
| 368 |
- def _init_font(self, desc):
|
|
| 387 |
+ def _init_font(self, desc: Pango.FontDescription) -> None:
|
|
| 369 | 388 |
font_button = Gtk.FontButton()
|
| 370 | 389 |
if desc is not None:
|
| 371 | 390 |
font_button.set_font_desc(desc)
|
| ... | ... | @@ -373,7 +392,7 @@ def _init_font(self, desc): |
| 373 | 392 |
font_button.connect('font-set', self._on_font_set)
|
| 374 | 393 |
self._box.add(font_button)
|
| 375 | 394 |
|
| 376 |
- def _on_color_set(self, color_button):
|
|
| 395 |
+ def _on_color_set(self, color_button: Gtk.ColorButton) -> None:
|
|
| 377 | 396 |
color = color_button.get_rgba()
|
| 378 | 397 |
color_string = color.to_string()
|
| 379 | 398 |
app.css_config.set_value(
|
| ... | ... | @@ -381,13 +400,13 @@ def _on_color_set(self, color_button): |
| 381 | 400 |
app.nec.push_incoming_event(NetworkEvent('style-changed'))
|
| 382 | 401 |
self.get_toplevel().reload_roster_theme()
|
| 383 | 402 |
|
| 384 |
- def _on_font_set(self, font_button):
|
|
| 403 |
+ def _on_font_set(self, font_button: Gtk.FontButton) -> None:
|
|
| 385 | 404 |
desc = font_button.get_font_desc()
|
| 386 | 405 |
app.css_config.set_font(self.option.selector, desc, pre=True)
|
| 387 | 406 |
app.nec.push_incoming_event(NetworkEvent('style-changed'))
|
| 388 | 407 |
self.get_toplevel().reload_roster_theme()
|
| 389 | 408 |
|
| 390 |
- def _on_remove(self, *args):
|
|
| 409 |
+ def _on_remove(self, *args: Any) -> None:
|
|
| 391 | 410 |
self.get_parent().remove(self)
|
| 392 | 411 |
app.css_config.remove_value(
|
| 393 | 412 |
self.option.selector, self.option.attr, pre=True)
|
| ... | ... | @@ -401,7 +420,7 @@ def __eq__(self, other): |
| 401 | 420 |
|
| 402 | 421 |
|
| 403 | 422 |
class ChooseOption(Gtk.ListBoxRow):
|
| 404 |
- def __init__(self, option):
|
|
| 423 |
+ def __init__(self, option: StyleOption) -> None:
|
|
| 405 | 424 |
Gtk.ListBoxRow.__init__(self)
|
| 406 | 425 |
self.option = option
|
| 407 | 426 |
label = Gtk.Label(label=option.label)
|