core: make getcwd() fail-safe
[git-cola.git] / cola / themes.py
blobd611933b4bcf6d3def5c30a045acbe1af902e32b
1 """Provides themes generator"""
2 from __future__ import absolute_import, division, unicode_literals
4 from qtpy import QtGui
6 from .i18n import N_
7 from .widgets import defs
8 from . import icons
9 from . import qtutils
12 class EStylesheet(object):
13 DEFAULT = 1
14 FLAT = 2
17 class Theme(object):
19 def __init__(self, name, hr_name, is_dark,
20 style_sheet=EStylesheet.DEFAULT, main_color=None):
21 self.name = name
22 self.hr_name = hr_name
23 self.is_dark = is_dark
24 self.style_sheet = style_sheet
25 self.main_color = main_color
27 def build_style_sheet(self, app_palette):
28 if self.style_sheet == EStylesheet.FLAT:
29 return self.style_sheet_flat()
30 else:
31 return self.style_sheet_default(app_palette)
33 def build_palette(self, app_palette):
34 QPalette = QtGui.QPalette
35 palette_dark = app_palette.color(QPalette.Base).lightnessF() < 0.5
37 if palette_dark and self.is_dark:
38 return app_palette
39 if not palette_dark and not self.is_dark:
40 return app_palette
41 if self.is_dark:
42 bg_color = QtGui.QColor('#202025')
43 else:
44 bg_color = QtGui.QColor('#edeef3')
46 txt_color = QtGui.QColor('#777')
47 palette = QPalette(bg_color)
48 palette.setColor(QPalette.Base, bg_color)
49 palette.setColor(QPalette.Disabled, QPalette.Text, txt_color)
50 return palette
52 @staticmethod
53 def style_sheet_default(palette):
54 window = palette.color(QtGui.QPalette.Window)
55 highlight = palette.color(QtGui.QPalette.Highlight)
56 shadow = palette.color(QtGui.QPalette.Shadow)
57 base = palette.color(QtGui.QPalette.Base)
59 window_rgb = qtutils.rgb_css(window)
60 highlight_rgb = qtutils.rgb_css(highlight)
61 shadow_rgb = qtutils.rgb_css(shadow)
62 base_rgb = qtutils.rgb_css(base)
64 return """
65 QCheckBox::indicator {
66 width: %(checkbox_size)spx;
67 height: %(checkbox_size)spx;
69 QCheckBox::indicator::unchecked {
70 border: %(checkbox_border)spx solid %(shadow_rgb)s;
71 background: %(base_rgb)s;
73 QCheckBox::indicator::checked {
74 image: url(%(checkbox_icon)s);
75 border: %(checkbox_border)spx solid %(shadow_rgb)s;
76 background: %(base_rgb)s;
79 QRadioButton::indicator {
80 width: %(radio_size)spx;
81 height: %(radio_size)spx;
83 QRadioButton::indicator::unchecked {
84 border: %(radio_border)spx solid %(shadow_rgb)s;
85 border-radius: %(radio_radius)spx;
86 background: %(base_rgb)s;
88 QRadioButton::indicator::checked {
89 image: url(%(radio_icon)s);
90 border: %(radio_border)spx solid %(shadow_rgb)s;
91 border-radius: %(radio_radius)spx;
92 background: %(base_rgb)s;
95 QSplitter::handle:hover {
96 background: %(highlight_rgb)s;
99 QMainWindow::separator {
100 background: %(window_rgb)s;
101 width: %(separator)spx;
102 height: %(separator)spx;
104 QMainWindow::separator:hover {
105 background: %(highlight_rgb)s;
108 """ % dict(separator=defs.separator,
109 window_rgb=window_rgb,
110 highlight_rgb=highlight_rgb,
111 shadow_rgb=shadow_rgb,
112 base_rgb=base_rgb,
113 checkbox_border=defs.border,
114 checkbox_icon=icons.check_name(),
115 checkbox_size=defs.checkbox,
116 radio_border=defs.radio_border,
117 radio_icon=icons.dot_name(),
118 radio_radius=defs.radio//2,
119 radio_size=defs.radio)
121 def style_sheet_flat(self):
122 main_color = self.main_color
123 color = QtGui.QColor(main_color)
124 color_rgb = qtutils.rgb_css(color)
126 if self.is_dark:
127 background = '#2e2f30'
128 field = '#383a3c'
129 grayed = '#06080a'
130 button_text = '#000000'
131 field_text = '#d0d0d0'
132 darker = qtutils.hsl_css(
133 color.hslHueF(),
134 color.hslSaturationF()*0.3,
135 color.lightnessF()*1.3
137 lighter = qtutils.hsl_css(
138 color.hslHueF(),
139 color.hslSaturationF()*0.7,
140 color.lightnessF()*0.6
142 focus = qtutils.hsl_css(
143 color.hslHueF(),
144 color.hslSaturationF()*0.7,
145 color.lightnessF()*0.7
147 else:
148 background = '#edeef3'
149 field = '#ffffff'
150 grayed = '#a2a2b0'
151 button_text = '#ffffff'
152 field_text = '#000000'
153 darker = qtutils.hsl_css(
154 color.hslHueF(),
155 color.hslSaturationF(),
156 color.lightnessF()*0.4
158 lighter = qtutils.hsl_css(
159 color.hslHueF(),
160 color.hslSaturationF()*2,
161 0.92
163 focus = color_rgb
165 return """
166 /* regular widgets */
168 background-color: %(background)s;
169 color: %(field_text)s;
170 selection-background-color: %(lighter)s;
171 alternate-background-color: %(field)s;
172 selection-color: %(field_text)s;
173 show-decoration-selected: 1;
174 spacing: 2px;
177 /* Focused widths get a thin border */
178 QTreeView:focus, QListView:focus,
179 QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {
180 border-width: 1px;
181 border-style: solid;
182 border-color: %(focus)s;
185 QWidget:disabled {
186 border-color: %(grayed)s;
187 color: %(grayed)s;
189 QDockWidget > QFrame {
190 margin: 0 2px 2px 2px;
191 min-height: 40px;
193 QPlainTextEdit, QLineEdit, QTextEdit, QAbstractItemView,
194 QAbstractSpinBox {
195 background-color: %(field)s;
196 border-color: %(grayed)s;
197 border-style: solid;
198 border-width: 1px;
200 QAbstractItemView::item:selected {
201 background-color: %(lighter)s;
203 QAbstractItemView::item:hover {
204 background-color: %(lighter)s;
206 QLabel {
207 color: %(darker)s;
208 background-color: transparent;
210 DockTitleBarWidget {
211 padding-bottom: 4px;
214 /* buttons */
215 QPushButton[flat="false"] {
216 background-color: %(button)s;
217 color: %(button_text)s;
218 border-radius: 2px;
219 border-width: 0;
220 margin-bottom: 1px;
221 min-width: 55px;
222 padding: 4px 5px;
224 QPushButton[flat="true"], QToolButton {
225 background-color: transparent;
226 border-radius: 0px;
228 QPushButton[flat="true"] {
229 margin-bottom: 10px;
231 QPushButton:hover, QToolButton:hover {
232 background-color: %(darker)s;
234 QPushButton[flat="false"]:pressed, QToolButton:pressed {
235 background-color: %(darker)s;
236 margin: 1px 1px 2px 1px;
238 QPushButton:disabled {
239 background-color: %(grayed)s;
240 color: %(field)s;
241 padding-left: 5px;
242 padding-top: 5px;
244 QPushButton[flat="true"]:disabled {
245 background-color: transparent;
248 /*menus*/
249 QMenuBar {
250 background-color: %(background)s;
251 color: %(field_text)s;
252 border-width: 0;
253 padding: 1px;
255 QMenuBar::item {
256 background: transparent;
258 QMenuBar::item:selected {
259 background: %(button)s;
261 QMenuBar::item:pressed {
262 background: %(button)s;
264 QMenu {
265 background-color: %(field)s;
267 QMenu::separator {
268 background: %(background)s;
269 height: 1px;
272 /* combo box */
273 QComboBox {
274 background-color: %(field)s;
275 border-color: %(grayed)s;
276 border-style: solid;
277 color: %(field_text)s;
278 border-radius: 0px;
279 border-width: 1px;
280 margin-bottom: 1px;
281 padding: 0 5px;
283 QComboBox::drop-down {
284 border-color: %(field_text)s %(field)s %(field)s %(field)s;
285 border-style: solid;
286 subcontrol-position: right;
287 border-width: 4px 3px 0 3px;
288 height: 0;
289 margin-right: 5px;
290 width: 0;
292 QComboBox::drop-down:hover {
293 border-color: %(button)s %(field)s %(field)s %(field)s;
295 QComboBox:item {
296 background-color: %(button)s;
297 color: %(button_text)s;
298 border-width: 0;
299 height: 22px;
301 QComboBox:item:selected {
302 background-color: %(darker)s;
303 color: %(button_text)s;
305 QComboBox:item:checked {
306 background-color: %(darker)s;
307 color: %(button_text)s;
310 /* MainWindow separator */
311 QMainWindow::separator {
312 width: %(separator)spx;
313 height: %(separator)spx;
315 QMainWindow::separator:hover {
316 background: %(focus)s;
319 /* scroll bar */
320 QScrollBar {
321 background-color: %(field)s;
322 border: 0;
324 QScrollBar::handle {
325 background: %(background)s
327 QScrollBar::handle:hover {
328 background: %(button)s
330 QScrollBar:horizontal {
331 margin: 0 11px 0 11px;
332 height: 10px;
334 QScrollBar:vertical {
335 margin: 11px 0 11px 0;
336 width: 10px;
338 QScrollBar::add-line, QScrollBar::sub-line {
339 background: %(background)s;
340 subcontrol-origin: margin;
342 QScrollBar::sub-line:horizontal { /*required by a buggy Qt version*/
343 subcontrol-position: left;
345 QScrollBar::add-line:hover, QScrollBar::sub-line:hover {
346 background: %(button)s;
348 QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
349 width: 10px;
351 QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
352 height: 10px;
354 QScrollBar:left-arrow, QScrollBar::right-arrow,
355 QScrollBar::up-arrow, QScrollBar::down-arrow {
356 border-style: solid;
357 height: 0;
358 width: 0;
360 QScrollBar:right-arrow {
361 border-color: %(background)s %(background)s
362 %(background)s %(darker)s;
363 border-width: 3px 0 3px 4px;
365 QScrollBar:left-arrow {
366 border-color: %(background)s %(darker)s
367 %(background)s %(background)s;
368 border-width: 3px 4px 3px 0;
370 QScrollBar:up-arrow {
371 border-color: %(background)s %(background)s
372 %(darker)s %(background)s;
373 border-width: 0 3px 4px 3px;
375 QScrollBar:down-arrow {
376 border-color: %(darker)s %(background)s
377 %(background)s %(background)s;
378 border-width: 4px 3px 0 3px;
380 QScrollBar:right-arrow:hover {
381 border-color: %(button)s %(button)s
382 %(button)s %(darker)s;
384 QScrollBar:left-arrow:hover {
385 border-color: %(button)s %(darker)s
386 %(button)s %(button)s;
388 QScrollBar:up-arrow:hover {
389 border-color: %(button)s %(button)s
390 %(darker)s %(button)s;
392 QScrollBar:down-arrow:hover {
393 border-color: %(darker)s %(button)s
394 %(button)s %(button)s;
397 /* tab bar (stacked & docked widgets) */
398 QTabBar::tab {
399 background: transparent;
400 border-color: %(darker)s;
401 border-width: 1px;
402 margin: 1px;
403 padding: 3px 5px;
405 QTabBar::tab:selected {
406 background: %(grayed)s;
409 /* check box */
410 QCheckBox {
411 spacing: 8px;
412 margin: 4px;
413 background-color: transparent;
415 QCheckBox::indicator {
416 background-color: %(field)s;
417 border-color: %(darker)s;
418 border-style: solid;
419 subcontrol-position: left;
420 border-width: 1px;
421 height: 13px;
422 width: 13px;
424 QCheckBox::indicator:unchecked:hover {
425 background-color: %(button)s;
427 QCheckBox::indicator:unchecked:pressed {
428 background-color: %(darker)s;
430 QCheckBox::indicator:checked {
431 background-color: %(darker)s;
433 QCheckBox::indicator:checked:hover {
434 background-color: %(button)s;
436 QCheckBox::indicator:checked:pressed {
437 background-color: %(field)s;
440 /* radio checkbox */
441 QRadioButton {
442 spacing: 8px;
443 margin: 4px;
445 QRadioButton::indicator {
446 height: 0.75em;
447 width: 0.75em;
450 /* progress bar */
451 QProgressBar {
452 background-color: %(field)s;
453 border: 1px solid %(darker)s;
455 QProgressBar::chunk {
456 background-color: %(button)s;
457 width: 1px;
460 /* spin box */
461 QAbstractSpinBox::up-button, QAbstractSpinBox::down-button {
462 background-color: transparent;
464 QAbstractSpinBox::up-arrow, QAbstractSpinBox::down-arrow {
465 border-style: solid;
466 height: 0;
467 width: 0;
469 QAbstractSpinBox::up-arrow {
470 border-color: %(field)s %(field)s %(darker)s %(field)s;
471 border-width: 0 3px 4px 3px;
473 QAbstractSpinBox::up-arrow:hover {
474 border-color: %(field)s %(field)s %(button)s %(field)s;
475 border-width: 0 3px 4px 3px;
477 QAbstractSpinBox::down-arrow {
478 border-color: %(darker)s %(field)s %(field)s %(field)s;
479 border-width: 4px 3px 0 3px;
481 QAbstractSpinBox::down-arrow:hover {
482 border-color: %(button)s %(field)s %(field)s %(field)s;
483 border-width: 4px 3px 0 3px;
486 /* dialogs */
487 QDialog > QFrame {
488 margin: 2px 2px 2px 2px;
491 /* headers */
492 QHeaderView {
493 color: %(field_text)s;
494 border-style: solid;
495 border-width: 0 0 1px 0;
496 border-color: %(grayed)s;
498 QHeaderView::section {
499 border-style: solid;
500 border-right: 1px solid %(grayed)s;
501 background-color: %(background)s;
502 color: %(field_text)s;
503 padding-left: 4px;
506 /* headers */
507 QHeaderView {
508 color: %(field_text)s;
509 border-style: solid;
510 border-width: 0 0 1px 0;
511 border-color: %(grayed)s;
513 QHeaderView::section {
514 border-style: solid;
515 border-right: 1px solid %(grayed)s;
516 background-color: %(background)s;
517 color: %(field_text)s;
518 padding-left: 4px;
521 """ % dict(background=background,
522 field=field,
523 button=color_rgb,
524 darker=darker,
525 lighter=lighter,
526 grayed=grayed,
527 button_text=button_text,
528 field_text=field_text,
529 separator=defs.separator,
530 focus=focus)
533 def get_all_themes():
534 return [
535 Theme('default', N_('Default'), False,
536 EStylesheet.DEFAULT, None),
537 Theme('flat-light-blue', N_('Flat light blue'),
538 False, EStylesheet.FLAT, '#5271cc'),
539 Theme('flat-light-red', N_('Flat light red'),
540 False, EStylesheet.FLAT, '#cc5452'),
541 Theme('flat-light-grey', N_('Flat light grey'),
542 False, EStylesheet.FLAT, '#707478'),
543 Theme('flat-light-green', N_('Flat light green'),
544 False, EStylesheet.FLAT, '#42a65c'),
545 Theme('flat-dark-blue', N_('Flat dark blue'),
546 True, EStylesheet.FLAT, '#5271cc'),
547 Theme('flat-dark-red', N_('Flat dark red'),
548 True, EStylesheet.FLAT, '#cc5452'),
549 Theme('flat-dark-grey', N_('Flat dark grey'),
550 True, EStylesheet.FLAT, '#aaaaaa'),
551 Theme('flat-dark-green', N_('Flat dark green'),
552 True, EStylesheet.FLAT, '#42a65c')
556 def options():
557 """Return a dictionary mapping display names to theme names"""
558 items = get_all_themes()
559 return [(item.hr_name, item.name) for item in items]
562 def find_theme(name):
563 themes = get_all_themes()
564 for item in themes:
565 if item.name == name:
566 return item
567 return themes[0]