1 """Themes generators"""
2 from __future__
import absolute_import
, division
, print_function
, unicode_literals
8 from .widgets
import defs
12 from . import resources
15 class EStylesheet(object):
18 CUSTOM
= 3 # Files located in ~/.config/git-cola/themes/*.qss
23 self
, name
, hr_name
, is_dark
, style_sheet
=EStylesheet
.DEFAULT
, main_color
=None
26 self
.hr_name
= hr_name
27 self
.is_dark
= is_dark
28 self
.is_palette_dark
= None
29 self
.style_sheet
= style_sheet
30 self
.main_color
= main_color
31 self
.disabled_text_color
= None
32 self
.text_color
= None
33 self
.highlight_color
= None
34 self
.background_color
= None
37 def build_style_sheet(self
, app_palette
):
38 if self
.style_sheet
== EStylesheet
.CUSTOM
:
39 return self
.style_sheet_custom(app_palette
)
40 elif self
.style_sheet
== EStylesheet
.FLAT
:
41 return self
.style_sheet_flat()
43 window
= app_palette
.color(QtGui
.QPalette
.Window
)
44 self
.is_palette_dark
= window
.lightnessF() < 0.5
45 return style_sheet_default(app_palette
)
47 def build_palette(self
, app_palette
):
48 QPalette
= QtGui
.QPalette
49 palette_dark
= app_palette
.color(QPalette
.Base
).lightnessF() < 0.5
50 if self
.is_palette_dark
is None:
51 self
.is_palette_dark
= palette_dark
53 if palette_dark
and self
.is_dark
:
54 self
.palette
= app_palette
56 if not palette_dark
and not self
.is_dark
:
57 self
.palette
= app_palette
60 background
= '#202025'
62 background
= '#edeef3'
64 bg_color
= qtutils
.css_color(background
)
65 txt_color
= qtutils
.css_color('#777777')
66 palette
= QPalette(bg_color
)
67 palette
.setColor(QPalette
.Base
, bg_color
)
68 palette
.setColor(QPalette
.Disabled
, QPalette
.Text
, txt_color
)
69 self
.background_color
= background
70 self
.palette
= palette
73 def style_sheet_flat(self
):
74 main_color
= self
.main_color
75 color
= qtutils
.css_color(main_color
)
76 color_rgb
= qtutils
.rgb_css(color
)
77 self
.is_palette_dark
= self
.is_dark
80 background
= '#2e2f30'
83 button_text
= '#000000'
84 field_text
= '#d0d0d0'
85 darker
= qtutils
.hsl_css(
86 color
.hslHueF(), color
.hslSaturationF() * 0.3, color
.lightnessF() * 1.3
88 lighter
= qtutils
.hsl_css(
89 color
.hslHueF(), color
.hslSaturationF() * 0.7, color
.lightnessF() * 0.6
91 focus
= qtutils
.hsl_css(
92 color
.hslHueF(), color
.hslSaturationF() * 0.7, color
.lightnessF() * 0.7
95 background
= '#edeef3'
98 button_text
= '#ffffff'
99 field_text
= '#000000'
100 darker
= qtutils
.hsl_css(
101 color
.hslHueF(), color
.hslSaturationF(), color
.lightnessF() * 0.4
103 lighter
= qtutils
.hsl_css(color
.hslHueF(), color
.hslSaturationF(), 0.92)
106 self
.disabled_text_color
= grayed
107 self
.text_color
= field_text
108 self
.highlight_color
= lighter
109 self
.background_color
= background
112 /* regular widgets */
114 background-color: %(background)s;
115 color: %(field_text)s;
116 selection-background-color: %(lighter)s;
117 alternate-background-color: %(field)s;
118 selection-color: %(field_text)s;
119 show-decoration-selected: 1;
123 /* Focused widths get a thin border */
124 QTreeView:focus, QListView:focus,
125 QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {
128 border-color: %(focus)s;
132 border-color: %(grayed)s;
135 QDockWidget > QFrame {
136 margin: 0 2px 2px 2px;
139 QPlainTextEdit, QLineEdit, QTextEdit, QAbstractItemView,
141 background-color: %(field)s;
142 border-color: %(grayed)s;
146 QAbstractItemView::item:selected {
147 background-color: %(lighter)s;
149 QAbstractItemView::item:hover {
150 background-color: %(lighter)s;
154 background-color: transparent;
161 QPushButton[flat="false"] {
162 background-color: %(button)s;
163 color: %(button_text)s;
170 QPushButton[flat="true"], QToolButton {
171 background-color: transparent;
174 QPushButton[flat="true"] {
177 QPushButton:hover, QToolButton:hover {
178 background-color: %(darker)s;
180 QPushButton[flat="false"]:pressed, QToolButton:pressed {
181 background-color: %(darker)s;
182 margin: 1px 1px 2px 1px;
184 QPushButton:disabled {
185 background-color: %(grayed)s;
190 QPushButton[flat="true"]:disabled {
191 background-color: transparent;
196 background-color: %(background)s;
197 color: %(field_text)s;
202 background: transparent;
204 QMenuBar::item:selected {
205 background: %(button)s;
207 QMenuBar::item:pressed {
208 background: %(button)s;
211 background-color: %(field)s;
214 background: %(background)s;
220 background-color: %(field)s;
221 border-color: %(grayed)s;
223 color: %(field_text)s;
229 QComboBox::drop-down {
230 border-color: %(field_text)s %(field)s %(field)s %(field)s;
232 subcontrol-position: right;
233 border-width: 4px 3px 0 3px;
238 QComboBox::drop-down:hover {
239 border-color: %(button)s %(field)s %(field)s %(field)s;
242 background-color: %(button)s;
243 color: %(button_text)s;
247 QComboBox:item:selected {
248 background-color: %(darker)s;
249 color: %(button_text)s;
251 QComboBox:item:checked {
252 background-color: %(darker)s;
253 color: %(button_text)s;
256 /* MainWindow separator */
257 QMainWindow::separator {
258 width: %(separator)spx;
259 height: %(separator)spx;
261 QMainWindow::separator:hover {
262 background: %(focus)s;
267 background-color: %(field)s;
271 background: %(background)s
273 QScrollBar::handle:hover {
274 background: %(button)s
276 QScrollBar:horizontal {
277 margin: 0 11px 0 11px;
280 QScrollBar:vertical {
281 margin: 11px 0 11px 0;
284 QScrollBar::add-line, QScrollBar::sub-line {
285 background: %(background)s;
286 subcontrol-origin: margin;
288 QScrollBar::sub-line:horizontal { /*required by a buggy Qt version*/
289 subcontrol-position: left;
291 QScrollBar::add-line:hover, QScrollBar::sub-line:hover {
292 background: %(button)s;
294 QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
297 QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
300 QScrollBar:left-arrow, QScrollBar::right-arrow,
301 QScrollBar::up-arrow, QScrollBar::down-arrow {
306 QScrollBar:right-arrow {
307 border-color: %(background)s %(background)s
308 %(background)s %(darker)s;
309 border-width: 3px 0 3px 4px;
311 QScrollBar:left-arrow {
312 border-color: %(background)s %(darker)s
313 %(background)s %(background)s;
314 border-width: 3px 4px 3px 0;
316 QScrollBar:up-arrow {
317 border-color: %(background)s %(background)s
318 %(darker)s %(background)s;
319 border-width: 0 3px 4px 3px;
321 QScrollBar:down-arrow {
322 border-color: %(darker)s %(background)s
323 %(background)s %(background)s;
324 border-width: 4px 3px 0 3px;
326 QScrollBar:right-arrow:hover {
327 border-color: %(button)s %(button)s
328 %(button)s %(darker)s;
330 QScrollBar:left-arrow:hover {
331 border-color: %(button)s %(darker)s
332 %(button)s %(button)s;
334 QScrollBar:up-arrow:hover {
335 border-color: %(button)s %(button)s
336 %(darker)s %(button)s;
338 QScrollBar:down-arrow:hover {
339 border-color: %(darker)s %(button)s
340 %(button)s %(button)s;
343 /* tab bar (stacked & docked widgets) */
345 background: transparent;
346 border-color: %(darker)s;
351 QTabBar::tab:selected {
352 background: %(grayed)s;
359 background-color: transparent;
361 QCheckBox::indicator {
362 background-color: %(field)s;
363 border-color: %(darker)s;
365 subcontrol-position: left;
370 QCheckBox::indicator:unchecked:hover {
371 background-color: %(button)s;
373 QCheckBox::indicator:unchecked:pressed {
374 background-color: %(darker)s;
376 QCheckBox::indicator:checked {
377 background-color: %(darker)s;
379 QCheckBox::indicator:checked:hover {
380 background-color: %(button)s;
382 QCheckBox::indicator:checked:pressed {
383 background-color: %(field)s;
391 QRadioButton::indicator {
398 background-color: %(field)s;
399 border: 1px solid %(darker)s;
401 QProgressBar::chunk {
402 background-color: %(button)s;
407 QAbstractSpinBox::up-button, QAbstractSpinBox::down-button {
408 background-color: transparent;
410 QAbstractSpinBox::up-arrow, QAbstractSpinBox::down-arrow {
415 QAbstractSpinBox::up-arrow {
416 border-color: %(field)s %(field)s %(darker)s %(field)s;
417 border-width: 0 3px 4px 3px;
419 QAbstractSpinBox::up-arrow:hover {
420 border-color: %(field)s %(field)s %(button)s %(field)s;
421 border-width: 0 3px 4px 3px;
423 QAbstractSpinBox::down-arrow {
424 border-color: %(darker)s %(field)s %(field)s %(field)s;
425 border-width: 4px 3px 0 3px;
427 QAbstractSpinBox::down-arrow:hover {
428 border-color: %(button)s %(field)s %(field)s %(field)s;
429 border-width: 4px 3px 0 3px;
434 margin: 2px 2px 2px 2px;
439 color: %(field_text)s;
441 border-width: 0 0 1px 0;
442 border-color: %(grayed)s;
444 QHeaderView::section {
446 border-right: 1px solid %(grayed)s;
447 background-color: %(background)s;
448 color: %(field_text)s;
454 color: %(field_text)s;
456 border-width: 0 0 1px 0;
457 border-color: %(grayed)s;
459 QHeaderView::section {
461 border-right: 1px solid %(grayed)s;
462 background-color: %(background)s;
463 color: %(field_text)s;
468 background
=background
,
474 button_text
=button_text
,
475 field_text
=field_text
,
476 separator
=defs
.separator
,
480 def style_sheet_custom(self
, app_palette
):
481 """Get custom style sheet.
482 File name is saved in variable self.name.
483 If user has deleted file, use default style"""
485 # check if path exists
486 filename
= resources
.config_home('themes', self
.name
+ '.qss')
487 if not core
.exists(filename
):
488 return style_sheet_default(app_palette
)
490 return core
.read(filename
)
491 except (IOError, OSError) as err
:
493 'warning: unable to read custom theme %s: %s' % (filename
, err
)
495 return style_sheet_default(app_palette
)
497 def get_palette(self
):
498 """Get a QPalette for the current theme"""
499 if self
.palette
is None:
500 palette
= qtutils
.current_palette()
502 palette
= self
.palette
505 def highlight_color_rgb(self
):
506 """Return an rgb(r,g,b) css color value for the selection highlight"""
507 if self
.highlight_color
:
508 highlight_rgb
= self
.highlight_color
509 elif self
.main_color
:
510 highlight_rgb
= qtutils
.rgb_css(
511 qtutils
.css_color(self
.main_color
).lighter()
514 palette
= self
.get_palette()
515 color
= palette
.color(QtGui
.QPalette
.Highlight
)
516 highlight_rgb
= qtutils
.rgb_css(color
)
519 def selection_color(self
):
520 """Return a color suitable for selections"""
521 highlight
= qtutils
.css_color(self
.highlight_color_rgb())
522 if highlight
.lightnessF() > 0.7: # Avoid clamping light colors to white.
525 color
= highlight
.lighter()
528 def text_colors_rgb(self
):
529 """Return a pair of rgb(r,g,b) css color values for text and selected text"""
531 text_rgb
= self
.text_color
532 highlight_text_rgb
= self
.text_color
534 palette
= self
.get_palette()
535 color
= palette
.text().color()
536 text_rgb
= qtutils
.rgb_css(color
)
538 color
= palette
.highlightedText().color()
539 highlight_text_rgb
= qtutils
.rgb_css(color
)
540 return text_rgb
, highlight_text_rgb
542 def disabled_text_color_rgb(self
):
543 """Return an rgb(r,g,b) css color value for the disabled text"""
544 if self
.disabled_text_color
:
545 disabled_text_rgb
= self
.disabled_text_color
547 palette
= self
.get_palette()
548 color
= palette
.color(QtGui
.QPalette
.Disabled
, QtGui
.QPalette
.Text
)
549 disabled_text_rgb
= qtutils
.rgb_css(color
)
550 return disabled_text_rgb
552 def background_color_rgb(self
):
553 """Return an rgb(r,g,b) css color value for the window background"""
554 if self
.background_color
:
555 background_color
= self
.background_color
557 palette
= self
.get_palette()
558 window
= palette
.color(QtGui
.QPalette
.Base
)
559 background_color
= qtutils
.rgb_css(window
)
560 return background_color
563 def style_sheet_default(palette
):
564 window
= palette
.color(QtGui
.QPalette
.Window
)
565 highlight
= palette
.color(QtGui
.QPalette
.Highlight
)
566 shadow
= palette
.color(QtGui
.QPalette
.Shadow
)
567 base
= palette
.color(QtGui
.QPalette
.Base
)
569 window_rgb
= qtutils
.rgb_css(window
)
570 highlight_rgb
= qtutils
.rgb_css(highlight
)
571 shadow_rgb
= qtutils
.rgb_css(shadow
)
572 base_rgb
= qtutils
.rgb_css(base
)
575 QCheckBox::indicator {
576 width: %(checkbox_size)spx;
577 height: %(checkbox_size)spx;
579 QCheckBox::indicator::unchecked {
580 border: %(checkbox_border)spx solid %(shadow_rgb)s;
581 background: %(base_rgb)s;
583 QCheckBox::indicator::checked {
584 image: url(%(checkbox_icon)s);
585 border: %(checkbox_border)spx solid %(shadow_rgb)s;
586 background: %(base_rgb)s;
589 QRadioButton::indicator {
590 width: %(radio_size)spx;
591 height: %(radio_size)spx;
593 QRadioButton::indicator::unchecked {
594 border: %(radio_border)spx solid %(shadow_rgb)s;
595 border-radius: %(radio_radius)spx;
596 background: %(base_rgb)s;
598 QRadioButton::indicator::checked {
599 image: url(%(radio_icon)s);
600 border: %(radio_border)spx solid %(shadow_rgb)s;
601 border-radius: %(radio_radius)spx;
602 background: %(base_rgb)s;
605 QSplitter::handle:hover {
606 background: %(highlight_rgb)s;
609 QMainWindow::separator {
610 background: %(window_rgb)s;
611 width: %(separator)spx;
612 height: %(separator)spx;
614 QMainWindow::separator:hover {
615 background: %(highlight_rgb)s;
619 separator
=defs
.separator
,
620 window_rgb
=window_rgb
,
621 highlight_rgb
=highlight_rgb
,
622 shadow_rgb
=shadow_rgb
,
624 checkbox_border
=defs
.border
,
625 checkbox_icon
=icons
.check_name(),
626 checkbox_size
=defs
.checkbox
,
627 radio_border
=defs
.radio_border
,
628 radio_icon
=icons
.dot_name(),
629 radio_radius
=defs
.radio
// 2,
630 radio_size
=defs
.radio
,
634 def get_all_themes():
640 style_sheet
=EStylesheet
.DEFAULT
,
645 N_('Flat light blue'),
647 style_sheet
=EStylesheet
.FLAT
,
648 main_color
='#5271cc',
652 N_('Flat light red'),
654 style_sheet
=EStylesheet
.FLAT
,
655 main_color
='#cc5452',
659 N_('Flat light grey'),
661 style_sheet
=EStylesheet
.FLAT
,
662 main_color
='#707478',
666 N_('Flat light green'),
668 style_sheet
=EStylesheet
.FLAT
,
669 main_color
='#42a65c',
673 N_('Flat dark blue'),
675 style_sheet
=EStylesheet
.FLAT
,
676 main_color
='#5271cc',
682 style_sheet
=EStylesheet
.FLAT
,
687 N_('Flat dark grey'),
689 style_sheet
=EStylesheet
.FLAT
,
690 main_color
='#aaaaaa',
694 N_('Flat dark green'),
696 style_sheet
=EStylesheet
.FLAT
,
697 main_color
='#42a65c',
701 # check if themes path exists in user folder
702 path
= resources
.config_home('themes')
703 if not os
.path
.isdir(path
):
706 # Gather Qt .qss stylesheet themes
708 filenames
= core
.listdir(path
)
712 for filename
in filenames
:
713 name
, ext
= os
.path
.splitext(filename
)
715 themes
.append(Theme(name
, N_(name
), False, EStylesheet
.CUSTOM
, None))
721 """Return a dictionary mapping display names to theme names"""
722 items
= get_all_themes()
723 return [(item
.hr_name
, item
.name
) for item
in items
]
726 def find_theme(name
):
727 themes
= get_all_themes()
729 if item
.name
== name
: