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: 0 2px 2px 2px;
138 QPlainTextEdit, QLineEdit, QTextEdit, QAbstractItemView,
140 background-color: %(field)s;
141 border-color: %(grayed)s;
145 QAbstractItemView::item:selected {
146 background-color: %(lighter)s;
148 QAbstractItemView::item:hover {
149 background-color: %(lighter)s;
153 background-color: transparent;
160 QPushButton[flat="false"] {
161 background-color: %(button)s;
162 color: %(button_text)s;
169 QPushButton[flat="true"], QToolButton {
170 background-color: transparent;
173 QPushButton[flat="true"] {
176 QPushButton:hover, QToolButton:hover {
177 background-color: %(darker)s;
179 QPushButton[flat="false"]:pressed, QToolButton:pressed {
180 background-color: %(darker)s;
181 margin: 1px 1px 2px 1px;
183 QPushButton:disabled {
184 background-color: %(grayed)s;
189 QPushButton[flat="true"]:disabled {
190 background-color: transparent;
195 background-color: %(background)s;
196 color: %(field_text)s;
201 background: transparent;
203 QMenuBar::item:selected {
204 background: %(button)s;
206 QMenuBar::item:pressed {
207 background: %(button)s;
210 background-color: %(field)s;
213 background: %(background)s;
219 background-color: %(field)s;
220 border-color: %(grayed)s;
222 color: %(field_text)s;
228 QComboBox::drop-down {
229 border-color: %(field_text)s %(field)s %(field)s %(field)s;
231 subcontrol-position: right;
232 border-width: 4px 3px 0 3px;
237 QComboBox::drop-down:hover {
238 border-color: %(button)s %(field)s %(field)s %(field)s;
241 background-color: %(button)s;
242 color: %(button_text)s;
246 QComboBox:item:selected {
247 background-color: %(darker)s;
248 color: %(button_text)s;
250 QComboBox:item:checked {
251 background-color: %(darker)s;
252 color: %(button_text)s;
255 /* MainWindow separator */
256 QMainWindow::separator {
257 width: %(separator)spx;
258 height: %(separator)spx;
260 QMainWindow::separator:hover {
261 background: %(focus)s;
266 background-color: %(field)s;
270 background: %(background)s
272 QScrollBar::handle:hover {
273 background: %(button)s
275 QScrollBar:horizontal {
276 margin: 0 11px 0 11px;
279 QScrollBar:vertical {
280 margin: 11px 0 11px 0;
283 QScrollBar::add-line, QScrollBar::sub-line {
284 background: %(background)s;
285 subcontrol-origin: margin;
287 QScrollBar::sub-line:horizontal { /*required by a buggy Qt version*/
288 subcontrol-position: left;
290 QScrollBar::add-line:hover, QScrollBar::sub-line:hover {
291 background: %(button)s;
293 QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal {
296 QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
299 QScrollBar:left-arrow, QScrollBar::right-arrow,
300 QScrollBar::up-arrow, QScrollBar::down-arrow {
305 QScrollBar:right-arrow {
306 border-color: %(background)s %(background)s
307 %(background)s %(darker)s;
308 border-width: 3px 0 3px 4px;
310 QScrollBar:left-arrow {
311 border-color: %(background)s %(darker)s
312 %(background)s %(background)s;
313 border-width: 3px 4px 3px 0;
315 QScrollBar:up-arrow {
316 border-color: %(background)s %(background)s
317 %(darker)s %(background)s;
318 border-width: 0 3px 4px 3px;
320 QScrollBar:down-arrow {
321 border-color: %(darker)s %(background)s
322 %(background)s %(background)s;
323 border-width: 4px 3px 0 3px;
325 QScrollBar:right-arrow:hover {
326 border-color: %(button)s %(button)s
327 %(button)s %(darker)s;
329 QScrollBar:left-arrow:hover {
330 border-color: %(button)s %(darker)s
331 %(button)s %(button)s;
333 QScrollBar:up-arrow:hover {
334 border-color: %(button)s %(button)s
335 %(darker)s %(button)s;
337 QScrollBar:down-arrow:hover {
338 border-color: %(darker)s %(button)s
339 %(button)s %(button)s;
342 /* tab bar (stacked & docked widgets) */
344 background: transparent;
345 border-color: %(darker)s;
350 QTabBar::tab:selected {
351 background: %(grayed)s;
358 background-color: transparent;
360 QCheckBox::indicator {
361 background-color: %(field)s;
362 border-color: %(darker)s;
364 subcontrol-position: left;
369 QCheckBox::indicator:unchecked:hover {
370 background-color: %(button)s;
372 QCheckBox::indicator:unchecked:pressed {
373 background-color: %(darker)s;
375 QCheckBox::indicator:checked {
376 background-color: %(darker)s;
378 QCheckBox::indicator:checked:hover {
379 background-color: %(button)s;
381 QCheckBox::indicator:checked:pressed {
382 background-color: %(field)s;
390 QRadioButton::indicator {
397 background-color: %(field)s;
398 border: 1px solid %(darker)s;
400 QProgressBar::chunk {
401 background-color: %(button)s;
406 QAbstractSpinBox::up-button, QAbstractSpinBox::down-button {
407 background-color: transparent;
409 QAbstractSpinBox::up-arrow, QAbstractSpinBox::down-arrow {
414 QAbstractSpinBox::up-arrow {
415 border-color: %(field)s %(field)s %(darker)s %(field)s;
416 border-width: 0 3px 4px 3px;
418 QAbstractSpinBox::up-arrow:hover {
419 border-color: %(field)s %(field)s %(button)s %(field)s;
420 border-width: 0 3px 4px 3px;
422 QAbstractSpinBox::down-arrow {
423 border-color: %(darker)s %(field)s %(field)s %(field)s;
424 border-width: 4px 3px 0 3px;
426 QAbstractSpinBox::down-arrow:hover {
427 border-color: %(button)s %(field)s %(field)s %(field)s;
428 border-width: 4px 3px 0 3px;
433 margin: 2px 2px 2px 2px;
438 color: %(field_text)s;
440 border-width: 0 0 1px 0;
441 border-color: %(grayed)s;
443 QHeaderView::section {
445 border-right: 1px solid %(grayed)s;
446 background-color: %(background)s;
447 color: %(field_text)s;
453 color: %(field_text)s;
455 border-width: 0 0 1px 0;
456 border-color: %(grayed)s;
458 QHeaderView::section {
460 border-right: 1px solid %(grayed)s;
461 background-color: %(background)s;
462 color: %(field_text)s;
467 background
=background
,
473 button_text
=button_text
,
474 field_text
=field_text
,
475 separator
=defs
.separator
,
479 def style_sheet_custom(self
, app_palette
):
480 """Get custom style sheet.
481 File name is saved in variable self.name.
482 If user has deleted file, use default style"""
484 # check if path exists
485 filename
= resources
.config_home('themes', self
.name
+ '.qss')
486 if not core
.exists(filename
):
487 return style_sheet_default(app_palette
)
489 return core
.read(filename
)
490 except (IOError, OSError) as err
:
492 'warning: unable to read custom theme %s: %s' % (filename
, err
)
494 return style_sheet_default(app_palette
)
496 def get_palette(self
):
497 """Get a QPalette for the current theme"""
498 if self
.palette
is None:
499 palette
= qtutils
.current_palette()
501 palette
= self
.palette
504 def highlight_color_rgb(self
):
505 """Return an rgb(r,g,b) css color value for the selection highlight"""
506 if self
.highlight_color
:
507 highlight_rgb
= self
.highlight_color
508 elif self
.main_color
:
509 highlight_rgb
= qtutils
.rgb_css(
510 qtutils
.css_color(self
.main_color
).lighter()
513 palette
= self
.get_palette()
514 color
= palette
.color(QtGui
.QPalette
.Highlight
)
515 highlight_rgb
= qtutils
.rgb_css(color
)
518 def selection_color(self
):
519 """Return a color suitable for selections"""
520 highlight
= qtutils
.css_color(self
.highlight_color_rgb())
521 if highlight
.lightnessF() > 0.7: # Avoid clamping light colors to white.
524 color
= highlight
.lighter()
527 def text_colors_rgb(self
):
528 """Return a pair of rgb(r,g,b) css color values for text and selected text"""
530 text_rgb
= self
.text_color
531 highlight_text_rgb
= self
.text_color
533 palette
= self
.get_palette()
534 color
= palette
.text().color()
535 text_rgb
= qtutils
.rgb_css(color
)
537 color
= palette
.highlightedText().color()
538 highlight_text_rgb
= qtutils
.rgb_css(color
)
539 return text_rgb
, highlight_text_rgb
541 def disabled_text_color_rgb(self
):
542 """Return an rgb(r,g,b) css color value for the disabled text"""
543 if self
.disabled_text_color
:
544 disabled_text_rgb
= self
.disabled_text_color
546 palette
= self
.get_palette()
547 color
= palette
.color(QtGui
.QPalette
.Disabled
, QtGui
.QPalette
.Text
)
548 disabled_text_rgb
= qtutils
.rgb_css(color
)
549 return disabled_text_rgb
551 def background_color_rgb(self
):
552 """Return an rgb(r,g,b) css color value for the window background"""
553 if self
.background_color
:
554 background_color
= self
.background_color
556 palette
= self
.get_palette()
557 window
= palette
.color(QtGui
.QPalette
.Base
)
558 background_color
= qtutils
.rgb_css(window
)
559 return background_color
562 def style_sheet_default(palette
):
563 window
= palette
.color(QtGui
.QPalette
.Window
)
564 highlight
= palette
.color(QtGui
.QPalette
.Highlight
)
565 shadow
= palette
.color(QtGui
.QPalette
.Shadow
)
566 base
= palette
.color(QtGui
.QPalette
.Base
)
568 window_rgb
= qtutils
.rgb_css(window
)
569 highlight_rgb
= qtutils
.rgb_css(highlight
)
570 shadow_rgb
= qtutils
.rgb_css(shadow
)
571 base_rgb
= qtutils
.rgb_css(base
)
574 QCheckBox::indicator {
575 width: %(checkbox_size)spx;
576 height: %(checkbox_size)spx;
578 QCheckBox::indicator::unchecked {
579 border: %(checkbox_border)spx solid %(shadow_rgb)s;
580 background: %(base_rgb)s;
582 QCheckBox::indicator::checked {
583 image: url(%(checkbox_icon)s);
584 border: %(checkbox_border)spx solid %(shadow_rgb)s;
585 background: %(base_rgb)s;
588 QRadioButton::indicator {
589 width: %(radio_size)spx;
590 height: %(radio_size)spx;
592 QRadioButton::indicator::unchecked {
593 border: %(radio_border)spx solid %(shadow_rgb)s;
594 border-radius: %(radio_radius)spx;
595 background: %(base_rgb)s;
597 QRadioButton::indicator::checked {
598 image: url(%(radio_icon)s);
599 border: %(radio_border)spx solid %(shadow_rgb)s;
600 border-radius: %(radio_radius)spx;
601 background: %(base_rgb)s;
604 QSplitter::handle:hover {
605 background: %(highlight_rgb)s;
608 QMainWindow::separator {
609 background: %(window_rgb)s;
610 width: %(separator)spx;
611 height: %(separator)spx;
613 QMainWindow::separator:hover {
614 background: %(highlight_rgb)s;
618 separator
=defs
.separator
,
619 window_rgb
=window_rgb
,
620 highlight_rgb
=highlight_rgb
,
621 shadow_rgb
=shadow_rgb
,
623 checkbox_border
=defs
.border
,
624 checkbox_icon
=icons
.check_name(),
625 checkbox_size
=defs
.checkbox
,
626 radio_border
=defs
.radio_border
,
627 radio_icon
=icons
.dot_name(),
628 radio_radius
=defs
.radio
// 2,
629 radio_size
=defs
.radio
,
633 def get_all_themes():
639 style_sheet
=EStylesheet
.DEFAULT
,
644 N_('Flat light blue'),
646 style_sheet
=EStylesheet
.FLAT
,
647 main_color
='#5271cc',
651 N_('Flat light red'),
653 style_sheet
=EStylesheet
.FLAT
,
654 main_color
='#cc5452',
658 N_('Flat light grey'),
660 style_sheet
=EStylesheet
.FLAT
,
661 main_color
='#707478',
665 N_('Flat light green'),
667 style_sheet
=EStylesheet
.FLAT
,
668 main_color
='#42a65c',
672 N_('Flat dark blue'),
674 style_sheet
=EStylesheet
.FLAT
,
675 main_color
='#5271cc',
681 style_sheet
=EStylesheet
.FLAT
,
682 main_color
='#cc5452',
686 N_('Flat dark grey'),
688 style_sheet
=EStylesheet
.FLAT
,
689 main_color
='#aaaaaa',
693 N_('Flat dark green'),
695 style_sheet
=EStylesheet
.FLAT
,
696 main_color
='#42a65c',
700 # check if themes path exists in user folder
701 path
= resources
.config_home('themes')
702 if not os
.path
.isdir(path
):
705 # Gather Qt .qss stylesheet themes
707 filenames
= core
.listdir(path
)
711 for filename
in filenames
:
712 name
, ext
= os
.path
.splitext(filename
)
714 themes
.append(Theme(name
, N_(name
), False, EStylesheet
.CUSTOM
, None))
720 """Return a dictionary mapping display names to theme names"""
721 items
= get_all_themes()
722 return [(item
.hr_name
, item
.name
) for item
in items
]
725 def find_theme(name
):
726 themes
= get_all_themes()
728 if item
.name
== name
: