Daniel Brötzmann pushed to branch master at gajim / gajim

Commits:

2 changed files:

Changes:

  • gajim/gtk/css_config.py
    ... ... @@ -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]

  • gajim/gtk/themes.py
    ... ... @@ -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)