krassowski/Anki-Night-Mode

View on GitHub
night_mode/styles.py

Summary

Maintainability
B
4 hrs
Test Coverage
from .config import ConfigValueGetter
from .internals import css, snake_case, SingletonMetaclass, RequiringMixin


class Style(RequiringMixin, metaclass=SingletonMetaclass):

    @property
    def name(self):
        return snake_case(self.__class__.__name__).split('_')[0]

    def __init__(self, app):
        RequiringMixin.__init__(self, app)
        self.app = app
        self.config = ConfigValueGetter(app.config)


class SharedStyles(Style):

    def __init__(self, app):
        super().__init__(app)
        self.build_styles()

    def refresh(self):
        self.build_styles()

    def build_styles(self):
        # TODO
        pass

    @css
    def top(self):
        return """
        html, #header
        {
            background:-webkit-gradient(linear, left top, left bottom, from(#333), to(#444));
            color:#eee
        }
        body, #header
        {
            border-bottom-color:#222
        }
        .hitem
        {
            color:#ddd
        }
        """

    @css
    def menu(self):
        return """
        QMenuBar,QMenu
        {
            background-color:#444!important;
            color:#eee!important
        }
        QMenuBar::item
        {
            background-color:transparent
        }
        QMenuBar::item:selected
        {
            background-color:""" + self.config.color_a + """!important;
            border-top-left-radius:6px;
            border-top-right-radius:6px
        }
        QMenu
        {
            border:1px solid #111
        }
        QMenu::item::selected
        {
            background-color:""" + self.config.color_a + """;
        }
        QMenu::item
        {
            padding:3px 25px 3px 25px;
            border:1px solid transparent
        }
        """

    @css
    def colors(self):
        return f'color: {self.config.color_t}; background-color: {self.config.color_b};'

    @css
    def colors_replacer(self):
        return """
        font[color="#007700"],span[style="color:#070"]
        {
            color:#00CC00!important
        }
        font[color="#000099"],span[style="color:#00F"]
        {
            color:#00BBFF!important
        }
        font[color="#C35617"],span[style="color:#c00"]
        {
            color:#D46728!important
        }
        font[color="#00a"]
        {
            color:#00BBFF
        }
        """

    @css
    def body_colors(self):
        """Generate and return CSS style of class "card"."""
        return (" body {    color:" + self.config.color_t + "!important;" +
                "background-color:" + self.config.color_b + "!important}")

    @css
    def user_color_map(self):
        style = ''
        for old, new in self.config.user_color_map.items():
            if old and new:
                style += f'font[color="{old}"]{{color: {new}!important}}'
        return style


class ButtonsStyle(Style):

    # Styling inspired by devgrow.com/dark-button-navigation-using-css3/ (thanks!)
    idle = """
        color:#AFB9C1;
        margin-top:0;
        position:relative;
        top:0;
        padding:3px 8px;
        border:1px solid #3E474D;
        border-top-color:#1c252b;
        border-left-color:#2d363c;
    """

    hover = 'color: #fff;'
    active = 'color: #fff;'

    @css
    def qt(self):
        return self.advanced_qt() + (self.qt_scrollbars if self.config.style_scroll_bars else '')

    def advanced_qt(self, restrict_to_parent='', restrict_to=''):
        return """
        """ + restrict_to_parent + """ QPushButton""" + restrict_to + """
        {
            background: qlineargradient(x1: 0.0, y1: 0.0, x2: 0.0, y2: 1.0, radius: 1, stop: 0.03 #3D4850, stop: 0.04 #313d45, stop: 1 #232B30);
            border-radius: 3px;
            """ + self.idle + """
        }
        """ + restrict_to_parent + """ QPushButton""" + restrict_to + """:hover
        {
            """ + self.hover + """
            background: qlineargradient(x1: 0.0, y1: 0.0, x2: 0.0, y2: 1.0, radius: 1, stop: 0.03 #4C5A64, stop: 0.04 #404F5A, stop: 1 #2E3940);
        }
        """ + restrict_to_parent + """ QPushButton""" + restrict_to + """:pressed
        {
            """ + self.active + """
            background: qlineargradient(x1: 0.0, y1: 0.0, x2: 0.0, y2: 1.0, radius: 1, stop: 0.03 #20282D, stop: 0.51 #252E34, stop: 1 #222A30);
        }
        """ + restrict_to_parent + """ QPushButton""" + restrict_to + """:disabled
        {
            """ + self.active + """
            background: qlineargradient(x1: 0.0, y1: 0.0, x2: 0.0, y2: 1.0, radius: 1, stop: 0.03 #20282D, stop: 0.51 #252E34, stop: 1 #222A30);
        }
        """ + restrict_to_parent + """ QPushButton""" + restrict_to + """:focus
        {
            outline: 1px dotted #4a90d9
        }
        """

    scrollbar_size = 15
    scrollbar_background = '#313d45'
    scrollbar_color = '#515d71'

    @css
    def qt_scrollbars(self):
        return f"""
        QScrollBar:horizontal, QScrollBar:vertical {{
            background: {self.scrollbar_background};
        }}
        QScrollBar:add-page, QScrollBar:sub-page{{
            background: {self.scrollbar_background};
        }}
        QScrollBar::handle:horizontal, QScrollBar::handle:vertical {{
            background: {self.scrollbar_color};
        }}
        QScrollBar {{
            margin: 0
        }}
        QScrollBar:vertical {{
            width: {self.scrollbar_size}px;
        }}
        QScrollBar:horizontal {{
            height: {self.scrollbar_size}px;
        }}
        QScrollBar::handle {{
            margin: 4px;
            border-radius: 3px
        }}
        QScrollBar::handle:vertical {{
            min-height: 20px;
        }}
        QScrollBar::handle:horizontal {{
            min-width: 20px;
        }}
        QScrollBar::add-line, QScrollBar::sub-line {{
            border: none;
            background: none;
        }}
        QScrollBar:left-arrow, QScrollBar::right-arrow, QScrollBar:up-arrow, QScrollBar::down-arrow {{
            border: none;
            background: none;
            color: none
        }}
        """

    @css
    def scrollbars(self):
        return f"""
        ::-webkit-scrollbar{{
            width: {self.scrollbar_size - 8}px;
        }}
        ::-webkit-scrollbar:horizontal {{
            height: {self.scrollbar_size - 8}px;
        }}
        ::-webkit-scrollbar-track {{
            background: {self.scrollbar_background};
        }}
        ::-webkit-scrollbar-thumb {{
            background: {self.scrollbar_color};
            border-radius: 4px;
        }}
        """

    @css
    def html(self):
        return f"""
        button
        {{
            { self.idle }
            text-shadow:1px 1px #1f272b;
            display: inline-block;
            background: #313d45;
            background: gradient(linear, left top, left bottom, color-stop(3%,#3D4850), color-stop(4%,#313d45), color-stop(100%,#232B30));
            box-shadow: 1px 1px 1px rgba(0,0,0,0.1);
            border-radius: 3px
        }}
        button:hover
        {{
            { self.hover }
            background: #404F5A);
            background: gradient(linear, left top, left bottom, color-stop(3%,#4C5A64), color-stop(4%,#404F5A), color-stop(100%,#2E3940));
        }}
        button:active
        {{
            { self.active }
            background: #252E34;
            background: gradient(linear, left top, left bottom, color-stop(3%,#20282D), color-stop(51%,#252E34), color-stop(100%,#222A30));
            box-shadow: 1px 1px 1px rgba(255,255,255,0.1);
        }}
        """ + (self.scrollbars if self.config.style_scroll_bars else '')


class DeckStyle(Style):
    require = {
        SharedStyles,
        ButtonsStyle
    }

    @css
    def bottom(self):
        return self.buttons.html + """
        #header
        {
            color:#ccc!important;
            background:-webkit-gradient(linear, left top, left bottom, from(#333), to(#222));
            border-top-color:#000;
            height:40px
        }
        """

    @css
    def style(self):
        return self.buttons.html + self.shared.colors_replacer + """
        a
        {
            color:#0099CC
        }
        .current
        {
            background-color:rgba(0,0,0,0.5)
        }
        a.deck, .collapse
        {
            color:#efe
        }
        tr.deck td
        {
            height:35px;
            border-bottom-color:#333
        }
        tr.deck font[color="#007700"]
        {
            color:#00CC00
        }
        tr.deck font[color="#000099"]
        {
            color:#00BBFF
        }
        .filtered
        {
            color:#00AAEE!important
        }
        .gears
        {
            filter: invert(1)
        }
        """


class MessageBoxStyle(Style):

    require = {
        ButtonsStyle
    }

    @css
    def style(self):
        """
        Generate and return CSS style of class QMessageBox,
        using global color declarations
        """
        return f"""
        QMessageBox,QLabel
        {{
            color: { self.config.color_t };
            background-color: { self.config.color_b }
        }}
        { self.buttons.qt }
        QPushButton
        {{
            min-width: 70px
        }}
        """


class ImageStyle(Style):

    @css
    def invert(self):
        return """
        img
        {
            filter:invert(1);
            -webkit-filter:invert(1)
        }
        """


class LatexStyle(Style):

    @css
    def invert(self):
        return """
        .latex
        {
            filter:invert(1);
            -webkit-filter:invert(1)
        }
        """


class DialogStyle(Style):

    require = {
        SharedStyles,
        ButtonsStyle
    }

    @css
    def style(self):
        return """
            QDialog,QLabel,QListWidget,QFontComboBox,QCheckBox,QSpinBox,QRadioButton,QHBoxLayout
            {
            """ + self.shared.colors + """
            }
            QFontComboBox::drop-down{border: 0px; border-left: 1px solid #555; width: 30px;}
            QFontComboBox::down-arrow{width:12px; height:8px;
                top:1px;
                image:url('""" + self.app.icons.arrow + """')
            }
            QFontComboBox, QSpinBox{border: 1px solid #555}

            QTabWidget QWidget
            {
                color:""" + self.config.color_t + """;
                background-color:#222;
                border-color:#555
            }
            QTabWidget QLabel {
                position:relative
            }
            QTabWidget QTabBar
            {
                color:#000
            }
            QTabWidget QTextEdit
            {
                border-color:#555
            }
            QTabWidget QGroupBox::title
            {
                subcontrol-origin: margin;
                subcontrol-position:top left;
                margin-top:-7px
            }
            """ + self.buttons.advanced_qt("QTabWidget")