diff --git a/data/gui/chat_control.ui b/data/gui/chat_control.ui index 5d5b97145..1d1140258 100644 --- a/data/gui/chat_control.ui +++ b/data/gui/chat_control.ui @@ -382,6 +382,7 @@ 5 + ChatControl-BannerEventBox True False @@ -411,6 +412,7 @@ vertical + ChatControl-BannerNameLabel True False <span weight="heavy" size="large">Contact name</span> @@ -425,6 +427,7 @@ + ChatControl-BannerLabel True False label diff --git a/data/gui/service_discovery_window.ui b/data/gui/service_discovery_window.ui index db01476f0..16fe77569 100644 --- a/data/gui/service_discovery_window.ui +++ b/data/gui/service_discovery_window.ui @@ -24,6 +24,7 @@ 6 + Discovery-BannerEventBox True False @@ -33,6 +34,7 @@ 6 + Discovery-BannerLabel True False 6 diff --git a/data/style/gajim.css b/data/style/gajim.css new file mode 100644 index 000000000..e69de29bb diff --git a/src/chat_control.py b/src/chat_control.py index 8db855f29..e944e7e58 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -1024,8 +1024,8 @@ class ChatControl(ChatControlBase): displaymarking = None if self.correcting: self.correcting = False - self.msg_textview.override_background_color( - Gtk.StateType.NORMAL, self.old_message_tv_color) + gtkgui_helpers.remove_css_class( + self.msg_textview, 'msgcorrectingcolor') self.print_conversation(message, self.contact.jid, encrypted=encrypted, xep0184_id=xep0184_id, xhtml=xhtml, @@ -1226,7 +1226,7 @@ class ChatControl(ChatControlBase): else: self.old_msg_kind = kind - def get_tab_label(self, chatstate): + def get_tab_label(self): unread = '' if self.resource: jid = self.contact.get_full_jid() @@ -1239,48 +1239,13 @@ class ChatControl(ChatControlBase): elif num_unread > 1: unread = '[' + str(num_unread) + ']' - # Draw tab label using chatstate - theme = gajim.config.get('roster_theme') - color_s = None - if not chatstate: - chatstate = self.contact.chatstate - if chatstate is not None: - if chatstate == 'composing': - color_s = gajim.config.get_per('themes', theme, - 'state_composing_color') - elif chatstate == 'inactive': - color_s = gajim.config.get_per('themes', theme, - 'state_inactive_color') - elif chatstate == 'gone': - color_s = gajim.config.get_per('themes', theme, - 'state_gone_color') - elif chatstate == 'paused': - color_s = gajim.config.get_per('themes', theme, - 'state_paused_color') - - context = self.parent_win.notebook.get_style_context() - if color_s: - # We set the color for when it's the current tab or not - color = Gdk.RGBA() - ok = Gdk.RGBA.parse(color, color_s) - if not ok: - del color - color = context.get_color(Gtk.StateFlags.ACTIVE) - # In inactive tab color to be lighter against the darker inactive - # background - if chatstate in ('inactive', 'gone') and\ - self.parent_win.get_active_control() != self: - color = self.lighten_color(color) - else: # active or not chatstate, get color from gtk - color = context.get_color(Gtk.StateFlags.ACTIVE) - name = self.contact.get_shown_name() if self.resource: name += '/' + self.resource label_str = GLib.markup_escape_text(name) if num_unread: # if unread, text in the label becomes bold label_str = '' + unread + label_str + '' - return (label_str, color) + return label_str def get_tab_image(self, count_unread=True): if self.resource: diff --git a/src/chat_control_base.py b/src/chat_control_base.py index d05d5ceb0..687819d28 100644 --- a/src/chat_control_base.py +++ b/src/chat_control_base.py @@ -158,7 +158,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): """ Derived types MAY implement this """ - self._paint_banner() self.draw_banner() def _update_banner_state_image(self): @@ -344,8 +343,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): self.msg_textview.drag_dest_set(Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT, self.dnd_list, Gdk.DragAction.COPY) - self.update_font() - # Hook up send button widget = self.xml.get_object('send_button') id_ = widget.connect('clicked', self._on_send_button_clicked) @@ -371,7 +368,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): if gajim.config.get('use_speller') and HAS_GTK_SPELL: self.set_speller() self.conv_textview.tv.show() - self._paint_banner() # For XEP-0172 self.user_nick = None @@ -503,76 +499,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): # send the message self.send_message(message, xhtml=xhtml) - def _paint_banner(self): - """ - Repaint banner with theme color - """ - theme = gajim.config.get('roster_theme') - bgcolor = gajim.config.get_per('themes', theme, 'bannerbgcolor') - textcolor = gajim.config.get_per('themes', theme, 'bannertextcolor') - # the backgrounds are colored by using an eventbox by - # setting the bg color of the eventbox and the fg of the name_label - banner_eventbox = self.xml.get_object('banner_eventbox') - banner_name_label = self.xml.get_object('banner_name_label') - self.disconnect_style_event(banner_name_label) - self.disconnect_style_event(self.banner_status_label) - if bgcolor: - color = Gdk.RGBA() - Gdk.RGBA.parse(color, bgcolor) - banner_eventbox.override_background_color(Gtk.StateType.NORMAL, - color) - default_bg = False - else: - default_bg = True - if textcolor: - color = Gdk.RGBA() - Gdk.RGBA.parse(color, textcolor) - banner_name_label.override_color(Gtk.StateType.NORMAL, - color) - self.banner_status_label.override_color( - Gtk.StateType.NORMAL, color) - default_fg = False - else: - default_fg = True - if default_bg or default_fg: - self._on_style_set_event(banner_name_label, None, default_fg, - default_bg) - if self.banner_status_label.get_realized(): - # Widget is realized - self._on_style_set_event(self.banner_status_label, None, default_fg, - default_bg) - - def disconnect_style_event(self, widget): - # Try to find the event_id - for id_ in self.handlers.keys(): - if self.handlers[id_] == widget: - widget.disconnect(id_) - del self.handlers[id_] - break - - def connect_style_event(self, widget, set_fg=False, set_bg=False): - self.disconnect_style_event(widget) - id_ = widget.connect('style-set', self._on_style_set_event, set_fg, - set_bg) - self.handlers[id_] = widget - - def _on_style_set_event(self, widget, style, *opts): - """ - Set style of widget from style class *.Frame.Eventbox - opts[0] == True -> set fg color - opts[1] == True -> set bg color - """ - banner_eventbox = self.xml.get_object('banner_eventbox') - self.disconnect_style_event(widget) - context = widget.get_style_context() - if opts[1]: - bg_color = context.get_background_color(Gtk.StateFlags.SELECTED) - banner_eventbox.override_background_color(Gtk.StateType.NORMAL, bg_color) - if opts[0]: - fg_color = context.get_color(Gtk.StateFlags.SELECTED) - widget.override_color(Gtk.StateType.NORMAL, fg_color) - self.connect_style_event(widget, opts[0], opts[1]) - def _conv_textview_key_press_event(self, widget, event): # translate any layout to latin_layout valid, entries = self.keymap.get_entries_for_keyval(event.keyval) @@ -968,11 +894,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): gtkgui_helpers.popup_emoticons_under_button(menu, widget, self.parent_win) - def update_font(self): - font = Pango.FontDescription(gajim.config.get('conversation_font')) - self.conv_textview.tv.override_font(font) - self.msg_textview.override_font(font) - def update_tags(self): self.conv_textview.update_tags() @@ -1226,20 +1147,15 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): msg_type == 'sent' and not self.correcting and (not \ history[pos - 1].startswith('/') or history[pos - 1].startswith('/me')): self.correcting = True - context = self.msg_textview.get_style_context() - state = Gtk.StateFlags.NORMAL - self.old_message_tv_color = context.get_background_color(state) - color = Gdk.RGBA() - Gdk.RGBA.parse(color, 'PaleGoldenrod') - self.msg_textview.override_background_color(Gtk.StateType.NORMAL, - color) + gtkgui_helpers.add_css_class( + self.msg_textview, 'msgcorrectingcolor') message = history[pos - 1] msg_buf.set_text(message) return if self.correcting: # We were previously correcting - self.msg_textview.override_background_color(Gtk.StateType.NORMAL, - self.old_message_tv_color) + gtkgui_helpers.remove_css_class( + self.msg_textview, 'msgcorrectingcolor') self.correcting = False pos += -1 if direction == 'up' else +1 if pos == -1: @@ -1258,14 +1174,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): message = '> %s\n' % message.replace('\n', '\n> ') msg_buf.set_text(message) - def lighten_color(self, color): - p = 0.4 - mask = 0 - color.red = int((color.red * p) + (mask * (1 - p))) - color.green = int((color.green * p) + (mask * (1 - p))) - color.blue = int((color.blue * p) + (mask * (1 - p))) - return color - def widget_set_visible(self, widget, state): """ Show or hide a widget diff --git a/src/common/config.py b/src/common/config.py index b35c44392..f627a4c11 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -468,6 +468,7 @@ class Config: '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' ], diff --git a/src/config.py b/src/config.py index 226e3e2f2..ae41646aa 100644 --- a/src/config.py +++ b/src/config.py @@ -750,6 +750,7 @@ class PreferencesWindow: # begin repainting themed widgets throughout gajim.interface.roster.repaint_themed_widgets() gajim.interface.roster.change_roster_style(None) + gtkgui_helpers.load_css() def update_theme_list(self): theme_combobox = self.xml.get_object('theme_combobox') @@ -880,14 +881,7 @@ class PreferencesWindow: else: font = '' gajim.config.set(text, font) - self.update_text_font() - - def update_text_font(self): - """ - Update text font in opened chat windows - """ - for ctrl in self._get_all_controls(): - ctrl.update_font() + gtkgui_helpers.load_css() def on_incoming_nick_colorbutton_color_set(self, widget): self.on_preference_widget_color_set(widget, 'inmsgcolor') diff --git a/src/conversation_textview.py b/src/conversation_textview.py index 065f59119..753d3c2d1 100644 --- a/src/conversation_textview.py +++ b/src/conversation_textview.py @@ -238,8 +238,8 @@ class ConversationTextview(GObject.GObject): self.change_cursor = False self.last_time_printout = 0 - font = Pango.FontDescription(gajim.config.get('conversation_font')) - self.tv.override_font(font) + style = self.tv.get_style_context() + style.add_class('font_custom') buffer_ = self.tv.get_buffer() end_iter = buffer_.get_end_iter() buffer_.create_mark('end', end_iter, False) diff --git a/src/dialogs.py b/src/dialogs.py index 2af26a88d..f794527e7 100644 --- a/src/dialogs.py +++ b/src/dialogs.py @@ -3379,10 +3379,6 @@ class XMLConsoleWindow: self.enabled = True self.xml.get_object('enable_checkbutton').set_active(True) - col = Gdk.RGBA() - Gdk.RGBA.parse(col, color) - self.input_textview.override_color(Gtk.StateType.NORMAL, col) - if len(gajim.connections) > 1: title = _('XML Console for %s') % self.account else: diff --git a/src/disco.py b/src/disco.py index 69341a186..0832e286e 100644 --- a/src/disco.py +++ b/src/disco.py @@ -561,7 +561,6 @@ _('Without a connection, you can not browse available services')) self.banner_eventbox = self.xml.get_object('banner_agent_eventbox') self.style_event_id = 0 self.banner.realize() - self.paint_banner() self.action_buttonbox = self.xml.get_object('action_buttonbox') # Address combobox @@ -660,63 +659,6 @@ _('Without a connection, you can not browse available services')) (markup, font.to_string(), text_after) self.banner.set_markup(markup) - def paint_banner(self): - """ - Repaint the banner with theme color - """ - theme = gajim.config.get('roster_theme') - bgcolor = gajim.config.get_per('themes', theme, 'bannerbgcolor') - textcolor = gajim.config.get_per('themes', theme, 'bannertextcolor') - self.disconnect_style_event() - if bgcolor: - color = Gdk.RGBA() - Gdk.RGBA.parse(color, bgcolor) - self.banner_eventbox.override_background_color(Gtk.StateType.NORMAL, - color) - default_bg = False - else: - default_bg = True - - if textcolor: - color = Gdk.RGBA() - Gdk.RGBA.parse(color, textcolor) - self.banner.override_color(Gtk.StateType.NORMAL, color) - default_fg = False - else: - default_fg = True - if default_fg or default_bg: - self._on_style_set_event(self.banner, None, default_fg, default_bg) - if self.browser: - self.browser.update_theme() - - def disconnect_style_event(self): - if self.style_event_id: - self.banner.disconnect(self.style_event_id) - self.style_event_id = 0 - - def connect_style_event(self, set_fg = False, set_bg = False): - self.disconnect_style_event() - self.style_event_id = self.banner.connect('style-set', - self._on_style_set_event, set_fg, set_bg) - - def _on_style_set_event(self, widget, style, *opts): - """ - Set style of widget from style class *.Frame.Eventbox - opts[0] == True -> set fg color - opts[1] == True -> set bg color - """ - self.disconnect_style_event() - context = widget.get_style_context() - if opts[1]: - bg_color = context.get_background_color(Gtk.StateFlags.SELECTED) - self.banner_eventbox.override_background_color(Gtk.StateType.NORMAL, - bg_color) - if opts[0]: - fg_color = context.get_color(Gtk.StateFlags.SELECTED) - self.banner.override_color(Gtk.StateType.NORMAL, fg_color) - self.banner.ensure_style() - self.connect_style_event(opts[0], opts[1]) - def destroy(self, chain = False): """ Close the browser. This can optionally close its children and propagate diff --git a/src/gajim.py b/src/gajim.py index 802157980..f06af138a 100644 --- a/src/gajim.py +++ b/src/gajim.py @@ -245,7 +245,9 @@ class GajimApplication(Gtk.Application): def do_activate(self): Gtk.Application.do_activate(self) from gui_interface import Interface + import gtkgui_helpers self.interface = Interface() + gtkgui_helpers.load_css() self.interface.run(self) self.add_actions() import gui_menu_builder diff --git a/src/groupchat_control.py b/src/groupchat_control.py index 6d11d7e82..8d38e1832 100644 --- a/src/groupchat_control.py +++ b/src/groupchat_control.py @@ -709,29 +709,17 @@ class GroupchatControl(ChatControlBase): has_focus = self.parent_win.window.get_property('has-toplevel-focus') current_tab = self.parent_win.get_active_control() == self - color_name = None color = None - theme = gajim.config.get('roster_theme') - context = self.parent_win.notebook.get_style_context() if chatstate == 'attention' and (not has_focus or not current_tab): self.attention_flag = True - color_name = gajim.config.get_per('themes', theme, - 'state_muc_directed_msg_color') - elif chatstate: - if chatstate == 'active' or (current_tab and has_focus): - self.attention_flag = False - # get active color from gtk - color = context.get_color(Gtk.StateFlags.ACTIVE) - elif chatstate == 'newmsg' and (not has_focus or not current_tab) \ - and not self.attention_flag: - color_name = gajim.config.get_per('themes', theme, - 'state_muc_msg_color') - if color_name: - color = Gdk.RGBA() - ok = Gdk.RGBA.parse(color, color_name) - if not ok: - del color - color = context.get_color(Gtk.StateFlags.ACTIVE) + color = 'state_muc_directed_msg_color' + elif chatstate == 'active' or (current_tab and has_focus): + self.attention_flag = False + # get active color from gtk + color = 'active' + elif chatstate == 'newmsg' and (not has_focus or not current_tab) \ + and not self.attention_flag: + color = 'state_muc_msg_color' if self.is_continued: # if this is a continued conversation @@ -1996,8 +1984,8 @@ class GroupchatControl(ChatControlBase): self.last_sent_msg = msg if self.correcting: self.correcting = False - self.msg_textview.override_background_color( - Gtk.StateType.NORMAL, self.old_message_tv_color) + gtkgui_helpers.remove_css_class( + self.msg_textview, 'msgcorrectingcolor') if self.correcting and self.last_sent_msg: correction_msg = self.last_sent_msg diff --git a/src/gtkgui_helpers.py b/src/gtkgui_helpers.py index 28e4075a9..3771bfb91 100644 --- a/src/gtkgui_helpers.py +++ b/src/gtkgui_helpers.py @@ -48,6 +48,7 @@ log = logging.getLogger('gajim.gtkgui_helpers') from common import i18n from common import gajim from common import pep +from common import configpaths gtk_icon_theme = Gtk.IconTheme.get_default() gtk_icon_theme.append_search_path(gajim.ICONS_DIR) @@ -1088,3 +1089,91 @@ def __label_size_allocate(widget, allocation): def get_action(action): return gajim.app.lookup_action(action) + +def load_css(): + path = os.path.join(configpaths.get('DATA'), 'style', 'gajim.css') + try: + with open(path, "r") as f: + css = f.read() + except Exception as exc: + print('Error loading css: %s', exc) + return + + provider = Gtk.CssProvider() + css = "\n".join((css, convert_config_to_css())) + provider.load_from_data(bytes(css.encode())) + Gtk.StyleContext.add_provider_for_screen( + Gdk.Screen.get_default(), + provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + +def convert_config_to_css(): + css = '' + themed_widgets = { + 'ChatControl-BannerEventBox': ('bannerbgcolor', 'background'), + 'ChatControl-BannerNameLabel': ('bannertextcolor', 'color'), + 'ChatControl-BannerLabel': ('bannertextcolor', 'color'), + 'Discovery-BannerEventBox': ('bannerbgcolor', 'background'), + 'Discovery-BannerLabel': ('bannertextcolor', 'color')} + + classes = {'state_composing_color': ('', 'color'), + 'state_inactive_color': ('', 'color'), + 'state_gone_color': ('', 'color'), + 'state_paused_color': ('', 'color'), + 'msgcorrectingcolor': ('text', 'background'), + 'state_muc_directed_msg_color': ('', 'color'), + 'state_muc_msg_color': ('', 'color')} + + + theme = gajim.config.get('roster_theme') + for key, values in themed_widgets.items(): + config, attr = values + css += '#{} {{'.format(key) + value = gajim.config.get_per('themes', theme, config) + if value: + css += '{attr}: {color};\n'.format(attr=attr, color=value) + css += '}\n' + + for key, values in classes.items(): + node, attr = values + value = gajim.config.get_per('themes', theme, key) + if value: + css += '.theme_{cls} {node} {{ {attr}: {color}; }}\n'.format( + cls=key, node=node, attr=attr, color=value) + + css += add_css_font() + + return css + +def add_css_class(widget, class_name): + style = widget.get_style_context() + for css_cls in style.list_classes(): + if css_cls.startswith('theme_'): + style.remove_class(css_cls) + if class_name: + style.add_class('theme_' + class_name) + +def remove_css_class(widget, class_name): + style = widget.get_style_context() + style.remove_class('theme_' + class_name) + +def add_css_font(): + conversation_font = gajim.config.get('conversation_font') + if not conversation_font: + return '' + font = Pango.FontDescription(conversation_font) + unit = "pt" if Gtk.check_version(3, 22, 0) is None else "px" + css = """ + .font_custom {{ + font-family: {family}; + font-size: {size}{unit}; + font-weight: {weight}; + }}""".format( + family=font.get_family(), + size=int(round(font.get_size() / Pango.SCALE)), + unit=unit, + weight=int(font.get_weight())) + css = css.replace("font-size: 0{unit};".format(unit=unit), "") + css = css.replace("font-weight: 0;", "") + css = "\n".join(filter(lambda x: x.strip(), css.splitlines())) + return css diff --git a/src/message_window.py b/src/message_window.py index 7e7c40857..e8f4ee5bc 100644 --- a/src/message_window.py +++ b/src/message_window.py @@ -39,6 +39,7 @@ import gtkgui_helpers import message_control import dialogs from chat_control_base import ChatControlBase +from chat_control import ChatControl from common import gajim from gtkgui_helpers import get_action @@ -636,7 +637,7 @@ class MessageWindow(object): window_mode == MessageWindowMgr.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER self.notebook.set_show_tabs(show_tabs_if_one_tab) - def redraw_tab(self, ctrl, chatstate = None): + def redraw_tab(self, ctrl, chatstate=None): tab = self.notebook.get_tab_label(ctrl.widget) if not tab: return @@ -653,11 +654,20 @@ class MessageWindow(object): # Update nick nick_label.set_max_width_chars(10) - (tab_label_str, tab_label_color) = ctrl.get_tab_label(chatstate) + if isinstance(ctrl, ChatControl): + tab_label_str = ctrl.get_tab_label() + # Set Label Color + class_name = 'state_{}_color'.format(chatstate) + gtkgui_helpers.add_css_class(nick_label, class_name) + else: + tab_label_str, color = ctrl.get_tab_label(chatstate) + # Set Label Color + if color == 'active': + gtkgui_helpers.add_css_class(nick_label, None) + elif color is not None: + gtkgui_helpers.add_css_class(nick_label, color) + nick_label.set_markup(tab_label_str) - if tab_label_color: - nick_label.override_color(Gtk.StateFlags.NORMAL, tab_label_color) - nick_label.override_color(Gtk.StateFlags.ACTIVE, tab_label_color) tab_img = ctrl.get_tab_image() if tab_img: diff --git a/src/roster_window.py b/src/roster_window.py index 235f7c8a7..f265252b3 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -4800,8 +4800,6 @@ class RosterWindow: for win in gajim.interface.msg_win_mgr.windows(): win.repaint_themed_widgets() for account in gajim.connections: - for addr in gajim.interface.instances[account]['disco']: - gajim.interface.instances[account]['disco'][addr].paint_banner() for ctrl in list(gajim.interface.minimized_controls[account].values()): ctrl.repaint_themed_widgets()