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 if self
.style_sheet
== EStylesheet
.FLAT
:
41 return self
.style_sheet_flat()
42 window
= app_palette
.color(QtGui
.QPalette
.Window
)
43 self
.is_palette_dark
= window
.lightnessF() < 0.5
44 return style_sheet_default(app_palette
)
46 def build_palette(self
, app_palette
):
47 QPalette
= QtGui
.QPalette
48 palette_dark
= app_palette
.color(QPalette
.Base
).lightnessF() < 0.5
49 if self
.is_palette_dark
is None:
50 self
.is_palette_dark
= palette_dark
52 if palette_dark
and self
.is_dark
:
53 self
.palette
= app_palette
55 if not palette_dark
and not self
.is_dark
:
56 self
.palette
= app_palette
59 background
= '#202025'
61 background
= '#edeef3'
63 bg_color
= qtutils
.css_color(background
)
64 txt_color
= qtutils
.css_color('#777777')
65 palette
= QPalette(bg_color
)
66 palette
.setColor(QPalette
.Base
, bg_color
)
67 palette
.setColor(QPalette
.Disabled
, QPalette
.Text
, txt_color
)
68 self
.background_color
= background
69 self
.palette
= palette
72 def style_sheet_flat(self
):
73 main_color
= self
.main_color
74 color
= qtutils
.css_color(main_color
)
75 color_rgb
= qtutils
.rgb_css(color
)
76 self
.is_palette_dark
= self
.is_dark
79 background
= '#2e2f30'
82 button_text
= '#000000'
83 field_text
= '#d0d0d0'
84 darker
= qtutils
.hsl_css(
85 color
.hslHueF(), color
.hslSaturationF() * 0.3, color
.lightnessF() * 1.3
87 lighter
= qtutils
.hsl_css(
88 color
.hslHueF(), color
.hslSaturationF() * 0.7, color
.lightnessF() * 0.6
90 focus
= qtutils
.hsl_css(
91 color
.hslHueF(), color
.hslSaturationF() * 0.7, color
.lightnessF() * 0.7
94 background
= '#edeef3'
97 button_text
= '#ffffff'
98 field_text
= '#000000'
99 darker
= qtutils
.hsl_css(
100 color
.hslHueF(), color
.hslSaturationF(), color
.lightnessF() * 0.4
102 lighter
= qtutils
.hsl_css(color
.hslHueF(), color
.hslSaturationF(), 0.92)
105 self
.disabled_text_color
= grayed
106 self
.text_color
= field_text
107 self
.highlight_color
= lighter
108 self
.background_color
= background
111 /* regular widgets */
113 background-color: %(background)s;
114 color: %(field_text)s;
115 selection-background-color: %(lighter)s;
116 alternate-background-color: %(field)s;
117 selection-color: %(field_text)s;
118 show-decoration-selected: 1;
122 /* Focused widths get a thin border */
123 QTreeView:focus, QListView:focus,
124 QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {
127 border-color: %(focus)s;
131 border-color: %(grayed)s;
134 QDockWidget > QFrame {
135 margin: 0px 0px 0px 0px;
137 QPlainTextEdit, QLineEdit, QTextEdit, QAbstractItemView,
139 background-color: %(field)s;
140 border-color: %(grayed)s;
144 QAbstractItemView::item:selected {
145 background-color: %(lighter)s;
147 QAbstractItemView::item:hover {
148 background-color: %(lighter)s;
152 background-color: transparent;
159 QPushButton[flat="false"] {
160 background-color: %(button)s;
161 color: %(button_text)s;
168 QPushButton[flat="true"], QToolButton {
169 background-color: transparent;
172 QPushButton[flat="true"] {
175 QPushButton:hover, QToolButton:hover {
176 background-color: %(darker)s;
178 QPushButton[flat="false"]:pressed, QToolButton:pressed {
179 background-color: %(darker)s;
180 margin: 1px 1px 2px 1px;
182 QPushButton:disabled {
183 background-color: %(grayed)s;
188 QPushButton[flat="true"]:disabled {
189 background-color: transparent;
194 background-color: %(background)s;
195 color: %(field_text)s;
200 background: transparent;
202 QMenuBar::item:selected {
203 background: %(button)s;
205 QMenuBar::item:pressed {
206 background: %(button)s;
209 background-color: %(field)s;
212 background: %(background)s;
218 background-color: %(field)s;
219 border-color: %(grayed)s;
221 color: %(field_text)s;
227 QComboBox::drop-down {
228 border-color: %(field_text)s %(field)s %(field)s %(field)s;
230 subcontrol-position: right;
231 border-width: 4px 3px 0 3px;
236 QComboBox::drop-down:hover {
237 border-color: %(button)s %(field)s %(field)s %(field)s;
240 background-color: %(button)s;
241 color: %(button_text)s;
245 QComboBox:item:selected {
246 background-color: %(darker)s;
247 color: %(button_text)s;
249 QComboBox:item:checked {
250 background-color: %(darker)s;
251 color: %(button_text)s;
254 /* MainWindow separator */
255 QMainWindow::separator {
256 width: %(separator)spx;
257 height: %(separator)spx;
259 QMainWindow::separator:hover {
260 background: %(focus)s;
265 background-color: %(field)s;
269 background: %(background)s
271 QScrollBar::handle:hover {
272 background: %(button)s
274 QScrollBar:horizontal {
275 margin: 0 11px 0 11px;
278 QScrollBar:vertical {
279 margin: 11px 0 11px 0;
282 QScrollBar::add-line, QScrollBar::sub-line {
283 background: %(background)s;
284 subcontrol-origin: margin;
286 QScrollBar::sub-line:horizontal { /*required by a buggy Qt version*/
287 subcontrol-position: left;
289 QScrollBar::add-line:hover, QScrollBar::sub-line:hover {
290 background: %(button)s;
292 QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
295 QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
298 QScrollBar:left-arrow, QScrollBar::right-arrow,
299 QScrollBar::up-arrow, QScrollBar::down-arrow {
304 QScrollBar:right-arrow {
305 border-color: %(background)s %(background)s
306 %(background)s %(darker)s;
307 border-width: 3px 0 3px 4px;
309 QScrollBar:left-arrow {
310 border-color: %(background)s %(darker)s
311 %(background)s %(background)s;
312 border-width: 3px 4px 3px 0;
314 QScrollBar:up-arrow {
315 border-color: %(background)s %(background)s
316 %(darker)s %(background)s;
317 border-width: 0 3px 4px 3px;
319 QScrollBar:down-arrow {
320 border-color: %(darker)s %(background)s
321 %(background)s %(background)s;
322 border-width: 4px 3px 0 3px;
324 QScrollBar:right-arrow:hover {
325 border-color: %(button)s %(button)s
326 %(button)s %(darker)s;
328 QScrollBar:left-arrow:hover {
329 border-color: %(button)s %(darker)s
330 %(button)s %(button)s;
332 QScrollBar:up-arrow:hover {
333 border-color: %(button)s %(button)s
334 %(darker)s %(button)s;
336 QScrollBar:down-arrow:hover {
337 border-color: %(darker)s %(button)s
338 %(button)s %(button)s;
341 /* tab bar (stacked & docked widgets) */
343 background: transparent;
344 border-color: %(darker)s;
349 QTabBar::tab:selected {
350 background: %(grayed)s;
357 background-color: transparent;
359 QCheckBox::indicator {
360 background-color: %(field)s;
361 border-color: %(darker)s;
363 subcontrol-position: left;
368 QCheckBox::indicator:unchecked:hover {
369 background-color: %(button)s;
371 QCheckBox::indicator:unchecked:pressed {
372 background-color: %(darker)s;
374 QCheckBox::indicator:checked {
375 background-color: %(darker)s;
377 QCheckBox::indicator:checked:hover {
378 background-color: %(button)s;
380 QCheckBox::indicator:checked:pressed {
381 background-color: %(field)s;
389 QRadioButton::indicator {
396 background-color: %(field)s;
397 border: 1px solid %(darker)s;
399 QProgressBar::chunk {
400 background-color: %(button)s;
405 QAbstractSpinBox::up-button, QAbstractSpinBox::down-button {
406 background-color: transparent;
408 QAbstractSpinBox::up-arrow, QAbstractSpinBox::down-arrow {
413 QAbstractSpinBox::up-arrow {
414 border-color: %(field)s %(field)s %(darker)s %(field)s;
415 border-width: 0 3px 4px 3px;
417 QAbstractSpinBox::up-arrow:hover {
418 border-color: %(field)s %(field)s %(button)s %(field)s;
419 border-width: 0 3px 4px 3px;
421 QAbstractSpinBox::down-arrow {
422 border-color: %(darker)s %(field)s %(field)s %(field)s;
423 border-width: 4px 3px 0 3px;
425 QAbstractSpinBox::down-arrow:hover {
426 border-color: %(button)s %(field)s %(field)s %(field)s;
427 border-width: 4px 3px 0 3px;
432 margin: 2px 2px 2px 2px;
437 color: %(field_text)s;
439 border-width: 0 0 1px 0;
440 border-color: %(grayed)s;
442 QHeaderView::section {
444 border-right: 1px solid %(grayed)s;
445 background-color: %(background)s;
446 color: %(field_text)s;
452 color: %(field_text)s;
454 border-width: 0 0 1px 0;
455 border-color: %(grayed)s;
457 QHeaderView::section {
459 border-right: 1px solid %(grayed)s;
460 background-color: %(background)s;
461 color: %(field_text)s;
466 'background': background
,
472 'button_text': button_text
,
473 'field_text': field_text
,
474 'separator': defs
.separator
,
478 def style_sheet_custom(self
, app_palette
):
479 """Get custom style sheet.
480 File name is saved in variable self.name.
481 If user has deleted file, use default style"""
483 # check if path exists
484 filename
= resources
.config_home('themes', self
.name
+ '.qss')
485 if not core
.exists(filename
):
486 return style_sheet_default(app_palette
)
488 return core
.read(filename
)
489 except (IOError, OSError) as err
:
491 'warning: unable to read custom theme %s: %s' % (filename
, err
)
493 return style_sheet_default(app_palette
)
495 def get_palette(self
):
496 """Get a QPalette for the current theme"""
497 if self
.palette
is None:
498 palette
= qtutils
.current_palette()
500 palette
= self
.palette
503 def highlight_color_rgb(self
):
504 """Return an rgb(r,g,b) css color value for the selection highlight"""
505 if self
.highlight_color
:
506 highlight_rgb
= self
.highlight_color
507 elif self
.main_color
:
508 highlight_rgb
= qtutils
.rgb_css(
509 qtutils
.css_color(self
.main_color
).lighter()
512 palette
= self
.get_palette()
513 color
= palette
.color(QtGui
.QPalette
.Highlight
)
514 highlight_rgb
= qtutils
.rgb_css(color
)
517 def selection_color(self
):
518 """Return a color suitable for selections"""
519 highlight
= qtutils
.css_color(self
.highlight_color_rgb())
520 if highlight
.lightnessF() > 0.7: # Avoid clamping light colors to white.
523 color
= highlight
.lighter()
526 def text_colors_rgb(self
):
527 """Return a pair of rgb(r,g,b) css color values for text and selected text"""
529 text_rgb
= self
.text_color
530 highlight_text_rgb
= self
.text_color
532 palette
= self
.get_palette()
533 color
= palette
.text().color()
534 text_rgb
= qtutils
.rgb_css(color
)
536 color
= palette
.highlightedText().color()
537 highlight_text_rgb
= qtutils
.rgb_css(color
)
538 return text_rgb
, highlight_text_rgb
540 def disabled_text_color_rgb(self
):
541 """Return an rgb(r,g,b) css color value for the disabled text"""
542 if self
.disabled_text_color
:
543 disabled_text_rgb
= self
.disabled_text_color
545 palette
= self
.get_palette()
546 color
= palette
.color(QtGui
.QPalette
.Disabled
, QtGui
.QPalette
.Text
)
547 disabled_text_rgb
= qtutils
.rgb_css(color
)
548 return disabled_text_rgb
550 def background_color_rgb(self
):
551 """Return an rgb(r,g,b) css color value for the window background"""
552 if self
.background_color
:
553 background_color
= self
.background_color
555 palette
= self
.get_palette()
556 window
= palette
.color(QtGui
.QPalette
.Base
)
557 background_color
= qtutils
.rgb_css(window
)
558 return background_color
561 def style_sheet_default(palette
):
562 window
= palette
.color(QtGui
.QPalette
.Window
)
563 highlight
= palette
.color(QtGui
.QPalette
.Highlight
)
564 shadow
= palette
.color(QtGui
.QPalette
.Shadow
)
565 base
= palette
.color(QtGui
.QPalette
.Base
)
567 window_rgb
= qtutils
.rgb_css(window
)
568 highlight_rgb
= qtutils
.rgb_css(highlight
)
569 shadow_rgb
= qtutils
.rgb_css(shadow
)
570 base_rgb
= qtutils
.rgb_css(base
)
573 QCheckBox::indicator {
574 width: %(checkbox_size)spx;
575 height: %(checkbox_size)spx;
577 QCheckBox::indicator::unchecked {
578 border: %(checkbox_border)spx solid %(shadow_rgb)s;
579 background: %(base_rgb)s;
581 QCheckBox::indicator::checked {
582 image: url(%(checkbox_icon)s);
583 border: %(checkbox_border)spx solid %(shadow_rgb)s;
584 background: %(base_rgb)s;
587 QRadioButton::indicator {
588 width: %(radio_size)spx;
589 height: %(radio_size)spx;
591 QRadioButton::indicator::unchecked {
592 border: %(radio_border)spx solid %(shadow_rgb)s;
593 border-radius: %(radio_radius)spx;
594 background: %(base_rgb)s;
596 QRadioButton::indicator::checked {
597 image: url(%(radio_icon)s);
598 border: %(radio_border)spx solid %(shadow_rgb)s;
599 border-radius: %(radio_radius)spx;
600 background: %(base_rgb)s;
603 QSplitter::handle:hover {
604 background: %(highlight_rgb)s;
607 QMainWindow::separator {
608 background: %(window_rgb)s;
609 width: %(separator)spx;
610 height: %(separator)spx;
612 QMainWindow::separator:hover {
613 background: %(highlight_rgb)s;
617 'separator': defs
.separator
,
618 'window_rgb': window_rgb
,
619 'highlight_rgb': highlight_rgb
,
620 'shadow_rgb': shadow_rgb
,
621 'base_rgb': base_rgb
,
622 'checkbox_border': defs
.border
,
623 'checkbox_icon': icons
.check_name(),
624 'checkbox_size': defs
.checkbox
,
625 'radio_border': defs
.radio_border
,
626 'radio_icon': icons
.dot_name(),
627 'radio_radius': defs
.radio
// 2,
628 'radio_size': defs
.radio
,
632 def get_all_themes():
638 style_sheet
=EStylesheet
.DEFAULT
,
643 N_('Flat light blue'),
645 style_sheet
=EStylesheet
.FLAT
,
646 main_color
='#5271cc',
650 N_('Flat light red'),
652 style_sheet
=EStylesheet
.FLAT
,
653 main_color
='#cc5452',
657 N_('Flat light grey'),
659 style_sheet
=EStylesheet
.FLAT
,
660 main_color
='#707478',
664 N_('Flat light green'),
666 style_sheet
=EStylesheet
.FLAT
,
667 main_color
='#42a65c',
671 N_('Flat dark blue'),
673 style_sheet
=EStylesheet
.FLAT
,
674 main_color
='#5271cc',
680 style_sheet
=EStylesheet
.FLAT
,
681 main_color
='#cc5452',
685 N_('Flat dark grey'),
687 style_sheet
=EStylesheet
.FLAT
,
688 main_color
='#aaaaaa',
692 N_('Flat dark green'),
694 style_sheet
=EStylesheet
.FLAT
,
695 main_color
='#42a65c',
699 # check if themes path exists in user folder
700 path
= resources
.config_home('themes')
701 if not os
.path
.isdir(path
):
704 # Gather Qt .qss stylesheet themes
706 filenames
= core
.listdir(path
)
710 for filename
in filenames
:
711 name
, ext
= os
.path
.splitext(filename
)
713 themes
.append(Theme(name
, N_(name
), False, EStylesheet
.CUSTOM
, None))
719 """Return a dictionary mapping display names to theme names"""
720 items
= get_all_themes()
721 return [(item
.hr_name
, item
.name
) for item
in items
]
724 def find_theme(name
):
725 themes
= get_all_themes()
727 if item
.name
== name
: