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):
......
This diff is collapsed.
......@@ -49,6 +49,7 @@ from gajim.common import i18n
from calendar import timegm
from gajim.common.fuzzyclock import FuzzyClock
from gajim import emoticons
from gajim.common.const import StyleAttr
from gajim.htmltextview import HtmlTextView
......@@ -216,42 +217,47 @@ class ConversationTextview(GObject.GObject):
self.last_time_printout = 0
style = self.tv.get_style_context()
style.add_class('font_custom')
style.add_class('gajim-conversation-font')
buffer_ = self.tv.get_buffer()
end_iter = buffer_.get_end_iter()
buffer_.create_mark('end', end_iter, False)
self.tagIn = buffer_.create_tag('incoming')
color = app.config.get('inmsgcolor')
font = Pango.FontDescription(app.config.get('inmsgfont'))
color = app.css_config.get_value(
'.gajim-incoming-nickname', StyleAttr.COLOR)
self.tagIn.set_property('foreground', color)
self.tagIn.set_property('font-desc', font)
desc = app.css_config.get_font('.gajim-incoming-nickname')
self.tagIn.set_property('font-desc', desc)
self.tagOut = buffer_.create_tag('outgoing')
color = app.config.get('outmsgcolor')
font = Pango.FontDescription(app.config.get('outmsgfont'))
color = app.css_config.get_value(
'.gajim-outgoing-nickname', StyleAttr.COLOR)
self.tagOut.set_property('foreground', color)
self.tagOut.set_property('font-desc', font)
desc = app.css_config.get_font('.gajim-outgoing-nickname')
self.tagOut.set_property('font-desc', desc)
self.tagStatus = buffer_.create_tag('status')
color = app.config.get('statusmsgcolor')
font = Pango.FontDescription(app.config.get('satusmsgfont'))
color = app.css_config.get_value(
'.gajim-status-message', StyleAttr.COLOR)
self.tagStatus.set_property('foreground', color)
self.tagStatus.set_property('font-desc', font)
desc = app.css_config.get_font('.gajim-status-message')
self.tagStatus.set_property('font-desc', desc)
self.tagInText = buffer_.create_tag('incomingtxt')
color = app.config.get('inmsgtxtcolor')
font = Pango.FontDescription(app.config.get('inmsgtxtfont'))
color = app.css_config.get_value(
'.gajim-incoming-message-text', StyleAttr.COLOR)
if color:
self.tagInText.set_property('foreground', color)
self.tagInText.set_property('font-desc', font)
desc = app.css_config.get_font('.gajim-incoming-message-text')
self.tagInText.set_property('font-desc', desc)
self.tagOutText = buffer_.create_tag('outgoingtxt')
color = app.config.get('outmsgtxtcolor')
color = app.css_config.get_value(
'.gajim-outgoing-message-text', StyleAttr.COLOR)
if color:
font = Pango.FontDescription(app.config.get('outmsgtxtfont'))
self.tagOutText.set_property('foreground', color)
self.tagOutText.set_property('font-desc', font)
self.tagOutText.set_property('foreground', color)
desc = app.css_config.get_font('.gajim-outgoing-message-text')
self.tagOutText.set_property('font-desc', desc)
colors = app.config.get('gc_nicknames_colors')
colors = colors.split(':')
......@@ -261,7 +267,8 @@ class ConversationTextview(GObject.GObject):
tag.set_property('foreground', color)
self.tagMarked = buffer_.create_tag('marked')
color = app.config.get('markedmsgcolor')
color = app.css_config.get_value(
'.gajim-highlight-message', StyleAttr.COLOR)
self.tagMarked.set_property('foreground', color)
self.tagMarked.set_property('weight', Pango.Weight.BOLD)
......@@ -276,7 +283,7 @@ class ConversationTextview(GObject.GObject):
tag.set_property('scale', 0.8333333333333)
tag = buffer_.create_tag('restored_message')
color = app.config.get('restored_messages_color')
color = app.css_config.get_value('.gajim-restored-message', StyleAttr.COLOR)
tag.set_property('foreground', color)
self.tv.create_tags()
......@@ -355,13 +362,13 @@ class ConversationTextview(GObject.GObject):
self.tv.destroy()
def update_tags(self):
self.tagIn.set_property('foreground', app.config.get('inmsgcolor'))
self.tagOut.set_property('foreground', app.config.get('outmsgcolor'))
self.tagIn.set_property('foreground', app.css_config.get_value('.gajim-incoming-nickname', StyleAttr.COLOR))
self.tagOut.set_property('foreground', app.css_config.get_value('.gajim-outgoing-nickname', StyleAttr.COLOR))
self.tagStatus.set_property('foreground',
app.config.get('statusmsgcolor'))
app.css_config.get_value('.gajim-status-message', StyleAttr.COLOR))
self.tagMarked.set_property('foreground',
app.config.get('markedmsgcolor'))
color = app.config.get('urlmsgcolor')
app.css_config.get_value('.gajim-highlight-message', StyleAttr.COLOR))
color = app.css_config.get_value('.gajim-url', StyleAttr.COLOR)
self.tv.tagURL.set_property('foreground', color)
self.tv.tagMail.set_property('foreground', color)
self.tv.tagXMPP.set_property('foreground', color)
......
......@@ -567,6 +567,9 @@
</child>
</object>
</child>
<style>
<class name="gajim-banner"/>
</style>
</object>
<packing>
<property name="expand">False</property>
......
This diff is collapsed.
......@@ -159,6 +159,9 @@
</child>
</object>
</child>
<style>
<class name="gajim-banner"/>
</style>
</object>
<packing>
<property name="expand">False</property>
......
This diff is collapsed.
......@@ -65,6 +65,9 @@ Agent JID - node</property>
</child>
</object>
</child>
<style>
<class name="gajim-banner"/>
</style>
</object>
<packing>
<property name="expand">False</property>
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.18"/>
<object class="GtkPopover" id="popover1">
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<property name="min_content_height">200</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkListBox" id="choose_option_listbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
<signal name="row-activated" handler="_add_option" swapped="no"/>
<style>
<class name="theme_popover_listbox"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<object class="GtkListStore" id="theme_store">
<columns>
<!-- column-name name -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkGrid" id="theme_grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">6</property>
<property name="column_spacing">6</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="theme_treeview">
<property name="width_request">150</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">theme_store</property>
<child internal-child="selection">
<object class="GtkTreeSelection">
<signal name="changed" handler="_on_theme_selected" swapped="no"/>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Themes</property>
<child>
<object class="GtkCellRendererText">
<property name="editable">True</property>
<signal name="edited" handler="_on_theme_name_edit" swapped="no"/>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="_on_add_new_theme" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-add-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="remove_theme_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="_on_remove_theme" swapped="no"/>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-remove-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="linked"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkListBox" id="option_listbox">
<property name="width_request">200</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
<property name="activate_on_single_click">False</property>
<style>
<class name="theme_listbox"/>
</style>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuButton" id="add_option_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="popover">popover1</property>