Commit 9d5131b8 authored by Philipp Hörist's avatar Philipp Hörist Committed by Philipp Hörist

Rework Gajim Theming

- Save all Theme settings to .css instead of the config file
- Add a gajim-dark.css
- Refactor the ThemesWindow
parent ee3cc9cb
......@@ -160,9 +160,7 @@ class GajimApplication(Gtk.Application):
self.interface.roster.window.present()
return
from gajim.gui_interface import Interface
from gajim import gtkgui_helpers
self.interface = Interface()
gtkgui_helpers.load_css()
self.interface.run(self)
self.add_actions()
from gajim import gui_menu_builder
......
......@@ -674,7 +674,6 @@ class ChatControl(ChatControlBase):
status_reduced = ''
status_escaped = GLib.markup_escape_text(status_reduced)
font_attrs, font_attrs_small = self.get_font_attrs()
st = app.config.get('displayed_chat_state_notifications')
cs = contact.chatstate
if cs and st in ('composing_only', 'all'):
......@@ -685,22 +684,21 @@ class ChatControl(ChatControlBase):
else:
chatstate = ''
label_text = '<span %s>%s</span><span %s>%s %s</span>' \
% (font_attrs, name, font_attrs_small, acct_info, chatstate)
label_text = '<span>%s</span><span size="x-small" weight="light">%s %s</span>' \
% (name, acct_info, chatstate)
if acct_info:
acct_info = i18n.direction_mark + ' ' + acct_info
label_tooltip = '%s%s %s' % (name, acct_info, chatstate)
else:
# weight="heavy" size="x-large"
label_text = '<span %s>%s</span><span %s>%s</span>' % \
(font_attrs, name, font_attrs_small, acct_info)
label_text = '<span>%s</span><span size="x-small" weight="light">%s</span>' % \
(name, acct_info)
if acct_info:
acct_info = i18n.direction_mark + ' ' + acct_info
label_tooltip = '%s%s' % (name, acct_info)
if status_escaped:
status_text = self.urlfinder.sub(self.make_href, status_escaped)
status_text = '<span %s>%s</span>' % (font_attrs_small, status_text)
status_text = '<span size="x-small" weight="light">%s</span>' % status_text
self.banner_status_label.set_tooltip_text(status)
self.banner_status_label.set_no_show_all(False)
self.banner_status_label.show()
......@@ -881,7 +879,7 @@ class ChatControl(ChatControlBase):
if self.correcting:
self.correcting = False
gtkgui_helpers.remove_css_class(
self.msg_textview, 'msgcorrectingcolor')
self.msg_textview, 'gajim-msg-correcting')
self.print_conversation(obj.message, self.contact.jid, tim=obj.timestamp,
encrypted=obj.encrypted, xep0184_id=xep0184_id, xhtml=obj.xhtml,
......
......@@ -38,6 +38,7 @@ from gi.repository import Gio
from gajim import gtkgui_helpers
from gajim import message_control
from gajim.gtk import NonModalConfirmationDialog
from gajim.gtk.util import convert_rgb_to_hex
from gajim import notify
import re
......@@ -52,6 +53,7 @@ from gajim.conversation_textview import ConversationTextview
from gajim.message_textview import MessageTextView
from gajim.common.contacts import GC_Contact
from gajim.common.connection_handlers_events import MessageOutgoingEvent
from gajim.common.const import StyleAttr
from gajim.command_system.implementation.middleware import ChatCommandProcessor
from gajim.command_system.implementation.middleware import CommandTools
......@@ -87,40 +89,13 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
keycode_ins = None
def make_href(self, match):
url_color = app.config.get('urlmsgcolor')
url_color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
color = convert_rgb_to_hex(url_color)
url = match.group()
if not '://' in url:
if '://' not in url:
url = 'http://' + url
return '<a href="%s"><span color="%s">%s</span></a>' % (url,
url_color, match.group())
def get_font_attrs(self):
"""
Get pango font attributes for banner from theme settings
"""
theme = app.config.get('roster_theme')
bannerfont = app.config.get_per('themes', theme, 'bannerfont')
bannerfontattrs = app.config.get_per('themes', theme, 'bannerfontattrs')
if bannerfont:
font = Pango.FontDescription(bannerfont)
else:
font = Pango.FontDescription('Normal')
if bannerfontattrs:
# B attribute is set by default
if 'B' in bannerfontattrs:
font.set_weight(Pango.Weight.HEAVY)
if 'I' in bannerfontattrs:
font.set_style(Pango.Style.ITALIC)
font_attrs = 'font_desc="%s"' % font.to_string()
# in case there is no font specified we use x-large font size
if font.get_size() == 0:
font_attrs = '%s size="x-large"' % font_attrs
font.set_weight(Pango.Weight.NORMAL)
font_attrs_small = 'font_desc="%s" size="small"' % font.to_string()
return (font_attrs, font_attrs_small)
return '<a href="%s"><span foreground="%s">%s</span></a>' % (
url, color, match.group())
def get_nb_unread(self):
jid = self.contact.jid
......@@ -381,6 +356,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
self._nec_ping)
app.ged.register_event_handler('sec-label-received', ged.GUI1,
self._sec_labels_received)
app.ged.register_event_handler('style-changed', ged.GUI1,
self._style_changed)
# This is basically a very nasty hack to surpass the inability
# to properly use the super, because of the old code.
......@@ -557,6 +534,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
self._nec_our_status)
app.ged.remove_event_handler('sec-label-received', ged.GUI1,
self._sec_labels_received)
app.ged.remove_event_handler('style-changed', ged.GUI1,
self._style_changed)
def on_msg_textview_populate_popup(self, textview, menu):
"""
......@@ -1099,6 +1079,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
def on_clear_formatting_menuitem_activate(self, widget):
self.msg_textview.clear_tags()
def _style_changed(self, *args):
self.update_tags()
def update_tags(self):
self.conv_textview.update_tags()
......@@ -1365,14 +1348,14 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
history[pos - 1].startswith('/') or history[pos - 1].startswith('/me')):
self.correcting = True
gtkgui_helpers.add_css_class(
self.msg_textview, 'msgcorrectingcolor')
self.msg_textview, 'gajim-msg-correcting')
message = history[pos - 1]
msg_buf.set_text(message)
return
if self.correcting:
# We were previously correcting
gtkgui_helpers.remove_css_class(
self.msg_textview, 'msgcorrectingcolor')
self.msg_textview, 'gajim-msg-correcting')
self.correcting = False
pos += -1 if direction == 'up' else +1
if pos == -1:
......
......@@ -43,6 +43,7 @@ from gajim.common import configpaths
from gajim.common import ged as ged_module
from gajim.common.contacts import LegacyContactsAPI
from gajim.common.events import Events
from gajim.common.css_config import CSSConfig
interface = None # The actual interface (the gtk one for the moment)
thread_interface = lambda *args: None # Interface to run a thread and then a callback
......@@ -66,6 +67,8 @@ gajimpaths = configpaths.gajimpaths
RecentGroupchat = namedtuple('RecentGroupchat', ['room', 'server', 'nickname'])
css_config = None
os_info = None # used to cache os information
transport_type = {} # list the type of transport
......@@ -625,3 +628,7 @@ def get_app_window(cls, account=None):
continue
return win
return None
def load_css_config():
global css_config
css_config = CSSConfig()
......@@ -92,13 +92,6 @@ class Config:
'mood_iconset': [ opt_str, DEFAULT_MOOD_ICONSET, '', True ],
'activity_iconset': [ opt_str, DEFAULT_ACTIVITY_ICONSET, '', True ],
'use_transports_iconsets': [ opt_bool, True, '', True ],
'inmsgcolor': [ opt_color, '#a40000', _('Incoming nickname color.'), True ],
'outmsgcolor': [ opt_color, '#3465a4', _('Outgoing nickname color.'), True ],
'inmsgtxtcolor': [ opt_color, '', _('Incoming text color.'), True ],
'outmsgtxtcolor': [ opt_color, '#555753', _('Outgoing text color.'), True ],
'statusmsgcolor': [ opt_color, '#4e9a06', _('Status message text color.'), True ],
'markedmsgcolor': [ opt_color, '#ff8080', '', True ],
'urlmsgcolor': [ opt_color, '#204a87', '', True ],
'notif_signin_color': [ opt_color, '#32CD32', _('Contact signed in notification color.') ], # limegreen
'notif_signout_color': [ opt_color, '#FF0000', _('Contact signout notification color') ], # red
'notif_message_color': [ opt_color, '#1E90FF', _('New message notification color.') ], # dodgerblue
......@@ -108,11 +101,6 @@ class Config:
'notif_invite_color': [ opt_color, '#D2B48C', _('Groupchat invitation notification color') ], # tan1
'notif_status_color': [ opt_color, '#D8BFD8', _('Background color of status changed notification') ], # thistle2
'notif_other_color': [ opt_color, '#FFFFFF', _('Other dialogs color.') ], # white
'inmsgfont': [ opt_str, '', _('Incoming nickname font.'), True ],
'outmsgfont': [ opt_str, '', _('Outgoing nickname font.'), True ],
'inmsgtxtfont': [ opt_str, '', _('Incoming text font.'), True ],
'outmsgtxtfont': [ opt_str, '', _('Outgoing text font.'), True ],
'statusmsgfont': [ opt_str, '', _('Status message text font.'), True ],
'collapsed_rows': [ opt_str, '', _('List (space separated) of rows (accounts and groups) that are collapsed.'), True ],
'roster_theme': [ opt_str, _('default'), '', True ],
'mergeaccounts': [ opt_bool, False, '', True ],
......@@ -205,7 +193,6 @@ class Config:
'notify_on_file_complete': [opt_bool, True],
'file_transfers_port': [opt_int, 28011],
'ft_add_hosts_to_send': [opt_str, '', _('Comma separated list of sent hosts, in addition of local interfaces, for File Transfer in case of address translation/port forwarding.')],
'conversation_font': [opt_str, ''],
'use_kib_mib': [opt_bool, False, _('IEC standard says KiB = 1024 bytes, KB = 1000 bytes.')],
'notify_on_all_muc_messages': [opt_bool, False],
'trayicon_notification_on_events': [opt_bool, True, _('Notify of events in the notification area.')],
......@@ -249,9 +236,6 @@ class Config:
'print_status_in_muc': [opt_str, 'none', _('Can be "none", "all" or "in_and_out". If "none", Gajim will no longer print status line in groupchats when a member changes their status and/or their status message. If "all" Gajim will print all status messages. If "in_and_out", Gajim will only print FOO enters/leaves group chat.')],
'log_contact_status_changes': [opt_bool, False],
'log_xhtml_messages': [opt_bool, False, _('Log XHTML messages instead of plain text messages.')],
'just_connected_bg_color': [opt_str, '#adc3c6', _('Background color of contacts when they just signed in.')],
'just_disconnected_bg_color': [opt_str, '#ab6161', _('Background color of contacts when they just signed out.')],
'restored_messages_color': [opt_color, '#555753'],
'restored_messages_small': [opt_bool, True, _('If true, restored messages will use a smaller font than the default one.')],
'hide_avatar_of_transport': [opt_bool, False, _('Don\'t show avatar for the transport itself.')],
'roster_window_skip_taskbar': [opt_bool, False, _('Don\'t show roster in the system taskbar.')],
......@@ -438,35 +422,7 @@ class Config:
'bosh_http_pipelining': [ opt_bool, False ],
'bosh_wait_for_restart_response': [ opt_bool, False ],
}, {}),
'themes': ({
'accounttextcolor': [ opt_color, 'black', '', True ],
'accountbgcolor': [ opt_color, 'white', '', True ],
'accountfont': [ opt_str, '', '', True ],
'accountfontattrs': [ opt_str, 'B', '', True ],
'grouptextcolor': [ opt_color, 'black', '', True ],
'groupbgcolor': [ opt_color, 'white', '', True ],
'groupfont': [ opt_str, '', '', True ],
'groupfontattrs': [ opt_str, 'I', '', True ],
'contacttextcolor': [ opt_color, 'black', '', True ],
'contactbgcolor': [ opt_color, 'white', '', True ],
'contactfont': [ opt_str, '', '', True ],
'contactfontattrs': [ opt_str, '', '', True ],
'bannertextcolor': [ opt_color, 'black', '', True ],
'bannerbgcolor': [ opt_color, '', '', True ],
'bannerfont': [ opt_str, '', '', True ],
'bannerfontattrs': [ opt_str, 'B', '', True ],
'msgcorrectingcolor': [opt_color, '#eee8aa'],
# http://www.pitt.edu/~nisg/cis/web/cgi/rgb.html
'state_inactive_color': [ opt_color, 'grey62' ],
'state_composing_color': [ opt_color, 'green4' ],
'state_paused_color': [ opt_color, 'mediumblue' ],
'state_gone_color': [ opt_color, 'grey' ],
# MUC chat states
'state_muc_msg_color': [ opt_color, 'mediumblue' ],
'state_muc_directed_msg_color': [ opt_color, 'red2' ],
}, {}),
'contacts': ({
'speller_language': [ opt_str, '', _('Language for which misspelled words will be checked')],
}, {}),
......@@ -522,29 +478,6 @@ class Config:
'muc_message_received': [ False, 'gc_message2.wav', _('Sound to play when any MUC message arrives.') ],
}
themes_default = {
# sorted alphanum
_('default'): [ '', '', '', 'B', '', '', '', 'I', '', '', '', '', '', '',
'', 'B' ],
_('green'): [ '', '#94aa8c', '', 'B', '#0000ff', '#eff3e7',
'', 'I', '#000000', '', '', '', '',
'#94aa8c', '', 'B' ],
_('grocery'): [ '', '#6bbe18', '', 'B', '#12125a', '#ceefad',
'', 'I', '#000000', '#efb26b', '', '', '',
'#108abd', '', 'B' ],
_('human'): [ '', '#996442', '', 'B', '#ab5920', '#e3ca94',
'', 'I', '#000000', '', '', '', '',
'#996442', '', 'B' ],
_('marine'): [ '', '#918caa', '', 'B', '', '#e9e7f3',
'', 'I', '#000000', '', '', '', '',
'#918caa', '', 'B' ],
}
proxies_default = {
_('Tor'): ['socks5', 'localhost', 9050],
}
......
......@@ -128,6 +128,7 @@ class ConfigPaths:
source_paths = [
('DATA', os.path.join(basedir, 'data')),
('STYLE', os.path.join(basedir, 'data', 'style')),
('GUI', os.path.join(basedir, 'data', 'gui')),
('ICONS', os.path.join(basedir, 'data', 'icons')),
('HOME', os.path.expanduser('~')),
......@@ -214,6 +215,10 @@ class ConfigPaths:
# Cache paths
('CACHE_DB', 'cache.db', PathLocation.CACHE, PathType.FILE),
('AVATAR', 'avatars', PathLocation.CACHE, PathType.FOLDER),
# Config paths
('MY_THEME', 'theme', PathLocation.CONFIG, PathType.FOLDER),
]
for path in paths:
......
......@@ -1378,3 +1378,11 @@ class InformationEvent(nec.NetworkIncomingEvent):
else:
self.args = (self.args,)
return True
class StyleChanged(nec.NetworkIncomingEvent):
name = 'style-changed'
base_network_events = []
def generate(self):
return True
from enum import IntEnum, unique
from enum import IntEnum, Enum, unique
from collections import namedtuple
Option = namedtuple('Option', 'kind label type value name callback data desc enabledif props')
Option.__new__.__defaults__ = (None,) * len(Option._fields)
DialogButton = namedtuple('DialogButton', 'text callback action')
DialogButton.__new__.__defaults__ = (None, None)
@unique
class OptionKind(IntEnum):
......@@ -117,6 +120,24 @@ class JIDConstant(IntEnum):
NORMAL_TYPE = 0
ROOM_TYPE = 1
@unique
class StyleAttr(Enum):
COLOR = 'color'
BACKGROUND = 'background'
FONT = 'font'
@unique
class CSSPriority(IntEnum):
APPLICATION = 600
APPLICATION_DARK = 601
DEFAULT_THEME = 610
DEFAULT_THEME_DARK = 611
USER_THEME = 650
@unique
class ButtonAction(Enum):
DESTRUCTIVE = 'destructive-action'
SUGGESTED = 'suggested-action'
@unique
class IdleState(IntEnum):
......
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Gajim.
#
# Gajim is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
import os
import math
import logging
import cssutils
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Pango
from gajim.common import app
from gajim.common import configpaths
from gajim.common.const import StyleAttr, CSSPriority
log = logging.getLogger('gajim.c.css')
_settings = Gtk.Settings.get_default()
PREFER_DARK = False
if _settings is not None:
PREFER_DARK = _settings.get_property('gtk-application-prefer-dark-theme')
class CSSConfig():
def __init__(self):
"""CSSConfig handles loading and storing of all relevant Gajim style files
The order in which CSSConfig loads the styles
1. gajim.css
2. gajim-dark.css (Only if gtk-application-prefer-dark-theme = True)
3. default.css or default-dark.css (from gajim/data/style)
4. user-theme.css (from ~/.config/Gajim/theme)
# gajim.css:
This is the main style and the application default
# gajim-dark.css
Has only entrys which we want to override in gajim.css
# default.css or default-dark.css
Has all the values that are changeable via UI (see themes.py).
Depending on `gtk-application-prefer-dark-theme` either default.css or
default-dark.css gets loaded
# user-theme.css
These are the themes the Themes Dialog stores. Because they are
loaded at last they overwrite everything else. Users should add custom
css here."""
# Delete empty rules
cssutils.ser.prefs.keepEmptyRules = False
# Holds the currently selected theme in the Theme Editor
self._pre_css = None
self._pre_css_path = None
# Holds the default theme, its used if values are not found
# in the selected theme
self._default_css = None
self._default_css_path = None
# Holds the currently selected theme
self._css = None
self._css_path = None
# User Theme CSS Provider
self._provider = Gtk.CssProvider()
# Cache of recently requested values
self._cache = {}
# Holds all currently available themes
self.themes = []
self._load_css()
self._gather_available_themes()
self._load_default()
self._load_selected()
self._activate_theme()
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
self._provider,
CSSPriority.USER_THEME)
def _load_css(self):
self._load_css_from_file('gajim.css', CSSPriority.APPLICATION)
if PREFER_DARK:
self._load_css_from_file('gajim-dark.css',
CSSPriority.APPLICATION_DARK)
self._load_css_from_file('default.css', CSSPriority.DEFAULT_THEME)
if PREFER_DARK:
self._load_css_from_file('default-dark.css',
CSSPriority.DEFAULT_THEME_DARK)
def _load_css_from_file(self, filename, priority):
path = os.path.join(configpaths.get('STYLE'), filename)
try:
with open(path, "r") as f:
css = f.read()
except Exception as exc:
log.error('Error loading css: %s', exc)
return
self._activate_css(css, priority)
def _activate_css(self, css, priority):
try:
provider = Gtk.CssProvider()
provider.load_from_data(bytes(css.encode('utf-8')))
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
provider,
priority)
except Exception:
log.exception('Error loading application css')
@staticmethod
def _pango_to_css_weight(number):
# Pango allows for weight values between 100 and 1000
# CSS allows only full hundred numbers like 100, 200 ..
number = int(number)
if number < 100:
return 100
if number > 900:
return 900
return int(math.ceil(number / 100.0)) * 100
def _gather_available_themes(self):
files = os.listdir(configpaths.get('MY_THEME'))
self.themes = [file[:-4] for file in files if file.endswith('.css')]
if 'default' in self.themes:
# Ignore user created themes that are named 'default'
self.themes.remove('default')
@classmethod
def get_theme_path(cls, theme, user=True):
if theme == 'default' and PREFER_DARK:
theme = 'default-dark'
if user:
return os.path.join(configpaths.get('MY_THEME'), '%s.css' % theme)
return os.path.join(configpaths.get('STYLE'), '%s.css' % theme)
def _determine_theme_path(self):
# Gets the path of the currently active theme.
# If it does not exist, it falls back to the default theme
theme = app.config.get('roster_theme')
if theme == 'default':
return self.get_theme_path(theme, user=False)
theme_path = self.get_theme_path(theme)
if not theme or not os.path.exists(theme_path):
log.warning('Theme %s not found, fallback to default', theme)
app.config.set('roster_theme', 'default')
log.info('Use Theme: default')
return self.get_theme_path('default', user=False)
log.info('Use Theme: %s', theme)
return theme_path
def _load_selected(self, new_path=None):
if new_path is None:
self._css_path = self._determine_theme_path()
else:
self._css_path = new_path
self._css = cssutils.parseFile(self._css_path)
def _load_default(self):
self._default_css_path = self.get_theme_path('default', user=False)
self._default_css = cssutils.parseFile(self._default_css_path)
def _load_pre(self, theme):
log.info('Preload theme %s', theme)
self._pre_css_path = self.get_theme_path(theme)
self._pre_css = cssutils.parseFile(self._pre_css_path)
def _write(self, pre):
path = self._css_path
css = self._css
if pre:
path = self._pre_css_path
css = self._pre_css
with open(path, 'w', encoding='utf-8') as file:
file.write(css.cssText.decode('utf-8'))
active = self._pre_css_path == self._css_path
if not pre or active:
self._load_selected()
self._activate_theme()
def set_value(self, selector, attr, value, pre=False):
if attr == StyleAttr.FONT:
# forward to set_font() for convenience
return self.set_font(selector, value, pre)
if isinstance(attr, StyleAttr):
attr = attr.value
css = self._css
if pre:
css = self._pre_css
for rule in css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Set %s %s %s', selector, attr, value)
rule.style[attr] = value
if not pre:
self._add_to_cache(selector, attr, value)
self._write(pre)
return
# The rule was not found, so we add it to this theme
log.info('Set %s %s %s', selector, attr, value)
rule = cssutils.css.CSSStyleRule(selectorText=selector)
rule.style[attr] = value
css.add(rule)
self._write(pre)
def set_font(self, selector, description, pre=False):
css = self._css
if pre:
css = self._pre_css
family, size, style, weight = self._get_attr_from_description(
description)
for rule in css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Set Font for: %s %s %s %s %s',
selector, family, size, style, weight)
rule.style['font-family'] = family
rule.style['font-style'] = style
rule.style['font-size'] = '%spt' % size
rule.style['font-weight'] = weight
if not pre:
self._add_to_cache(
selector, 'fontdescription', description)
self._write(pre)
return
# The rule was not found, so we add it to this theme
log.info('Set Font for: %s %s %s %s %s',
selector, family, size, style, weight)
rule = cssutils.css.CSSStyleRule(selectorText=selector)
rule.style['font-family'] = family
rule.style['font-style'] = style
rule.style['font-size'] = '%spt' % size
rule.style['font-weight'] = weight
css.add(rule)
self._write(pre)
def _get_attr_from_description(self, description):
size = description.get_size() / Pango.SCALE
style = self._get_string_from_pango_style(description.get_style())
weight = self._pango_to_css_weight(int(description.get_weight()))
family = description.get_family()
return family, size, style, weight
def _get_default_rule(self, selector, attr):
for rule in self._default_css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Get Default Rule %s', selector)
return rule
def get_font(self, selector, pre=False):
if pre:
css = self._pre_css
else:
css = self._css
try:
return self._get_from_cache(selector, 'fontdescription')
except KeyError:
pass
if css is None:
return
for rule in css:
if rule.type != rule.STYLE_RULE:
continue
if rule.selectorText == selector:
log.info('Get Font for: %s', selector)
style = rule.style.getPropertyValue('font-style') or None
size = rule.style.getPropertyValue('font-size') or None
weight = rule.style.getPropertyValue('font-weight') or None
family = rule.style.getPropertyValue('font-family') or None
desc = self._get_description_from_css(
family, size, style, weight)
if not pre:
self._add_to_cache(selector, 'fontdescription', desc)
return desc
def _get_description_from_css(self, family, size, style, weight):
if family is None:
return
desc = Pango.FontDescription()
desc.set_family(family)
if weight is not None:
desc.set_weight(Pango.Weight(int(weight)))
if style is not None:
desc.set_style(self._get_pango_style_from_string(style))
if size is not None:
desc.set_size(int(size[:-2]) * Pango.SCALE)
return desc
@staticmethod
def _get_pango_style_from_string(style: str) -> int:
if style == 'normal':
return Pango.Style(0)
if style == 'oblique':
return Pango.Style(1)
# Pango.Style.ITALIC:
return Pango.Style(2)