diff --git a/src/dialog/veditsnippetdialog.cpp b/src/dialog/veditsnippetdialog.cpp index 6c0cac0d..7a7a7098 100644 --- a/src/dialog/veditsnippetdialog.cpp +++ b/src/dialog/veditsnippetdialog.cpp @@ -39,7 +39,7 @@ void VEditSnippetDialog::setupUI(const QString &p_title, const QString &p_info) m_nameEdit->setValidator(validator); // Type. - m_typeCB = new QComboBox(); + m_typeCB = VUtils::getComboBox(); for (int i = 0; i < VSnippet::Type::Invalid; ++i) { m_typeCB->addItem(VSnippet::typeStr(static_cast(i)), i); } @@ -49,7 +49,7 @@ void VEditSnippetDialog::setupUI(const QString &p_title, const QString &p_info) m_typeCB->setCurrentIndex(typeIdx); // Shortcut. - m_shortcutCB = new QComboBox(); + m_shortcutCB = VUtils::getComboBox(); m_shortcutCB->addItem(tr("None"), QChar()); auto shortcuts = getAvailableShortcuts(); for (auto it : shortcuts) { diff --git a/src/dialog/vnewfiledialog.cpp b/src/dialog/vnewfiledialog.cpp index 96138379..dd5486e1 100644 --- a/src/dialog/vnewfiledialog.cpp +++ b/src/dialog/vnewfiledialog.cpp @@ -52,7 +52,7 @@ void VNewFileDialog::setupUI(const QString &p_title, m_nameEdit->setSelection(0, (dotIndex == -1) ? p_defaultName.size() : dotIndex); // Template. - m_templateCB = new QComboBox(); + m_templateCB = VUtils::getComboBox(); m_templateCB->setToolTip(tr("Choose a template (magic word supported)")); m_templateCB->setSizeAdjustPolicy(QComboBox::AdjustToContents); diff --git a/src/dialog/vsettingsdialog.cpp b/src/dialog/vsettingsdialog.cpp index 5191f219..8db4ad45 100644 --- a/src/dialog/vsettingsdialog.cpp +++ b/src/dialog/vsettingsdialog.cpp @@ -161,7 +161,7 @@ VGeneralTab::VGeneralTab(QWidget *p_parent) : QWidget(p_parent) { // Language combo. - m_langCombo = new QComboBox(this); + m_langCombo = VUtils::getComboBox(this); m_langCombo->setToolTip(tr("Choose the language of VNote interface")); m_langCombo->addItem(tr("System"), "System"); auto langs = VUtils::getAvailableLanguages(); @@ -194,7 +194,7 @@ VGeneralTab::VGeneralTab(QWidget *p_parent) QLayout *VGeneralTab::setupStartupPagesLayout() { - m_startupPageTypeCombo = new QComboBox(this); + m_startupPageTypeCombo = VUtils::getComboBox(this); m_startupPageTypeCombo->setToolTip(tr("Restore tabs or open specific notes on startup")); m_startupPageTypeCombo->addItem(tr("None"), (int)StartupPageType::None); m_startupPageTypeCombo->addItem(tr("Continue where you left off"), (int)StartupPageType::ContinueLeftOff); @@ -598,19 +598,19 @@ VMarkdownTab::VMarkdownTab(QWidget *p_parent) : QWidget(p_parent) { // Default note open mode. - m_openModeCombo = new QComboBox(); + m_openModeCombo = VUtils::getComboBox(); m_openModeCombo->setToolTip(tr("Default mode to open a note")); m_openModeCombo->addItem(tr("Read Mode"), (int)OpenFileMode::Read); m_openModeCombo->addItem(tr("Edit Mode"), (int)OpenFileMode::Edit); // Heading sequence. - m_headingSequenceTypeCombo = new QComboBox(); + m_headingSequenceTypeCombo = VUtils::getComboBox(); m_headingSequenceTypeCombo->setToolTip(tr("Enable auto sequence for all headings (in the form like 1.2.3.4.)")); m_headingSequenceTypeCombo->addItem(tr("Disabled"), (int)HeadingSequenceType::Disabled); m_headingSequenceTypeCombo->addItem(tr("Enabled"), (int)HeadingSequenceType::Enabled); m_headingSequenceTypeCombo->addItem(tr("Enabled for notes only"), (int)HeadingSequenceType::EnabledNoteOnly); - m_headingSequenceLevelCombo = new QComboBox(); + m_headingSequenceLevelCombo = VUtils::getComboBox(); m_headingSequenceLevelCombo->setToolTip(tr("Base level to start heading sequence")); m_headingSequenceLevelCombo->addItem(tr("1"), 1); m_headingSequenceLevelCombo->addItem(tr("2"), 2); diff --git a/src/main.cpp b/src/main.cpp index f2734602..51e453e5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,9 +12,12 @@ #include "utils/vutils.h" #include "vsingleinstanceguard.h" #include "vconfigmanager.h" +#include "vpalette.h" VConfigManager *g_config; +VPalette *g_palette; + #if defined(QT_NO_DEBUG) // 5MB log size. #define MAX_LOG_SIZE 5 * 1024 * 1024 @@ -164,10 +167,12 @@ int main(int argc, char *argv[]) app.installTranslator(&translator); } + VPalette palette(g_config->getThemeFile()); + g_palette = &palette; + VMainWindow w(&guard); - QString style = VUtils::readFileFromDisk(":/resources/vnote.qss"); + QString style = palette.fetchQtStyleSheet(); if (!style.isEmpty()) { - VUtils::processStyle(style, w.getPalette()); app.setStyleSheet(style); } diff --git a/src/resources/icons/arrow_dropup.svg b/src/resources/icons/arrow_dropup.svg deleted file mode 100644 index 5cdfca0c..00000000 --- a/src/resources/icons/arrow_dropup.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/src/resources/icons/arrow_dropdown.svg b/src/resources/themes/v_white/arrow_dropdown.svg similarity index 98% rename from src/resources/icons/arrow_dropdown.svg rename to src/resources/themes/v_white/arrow_dropdown.svg index d46e9629..55ee520d 100644 --- a/src/resources/icons/arrow_dropdown.svg +++ b/src/resources/themes/v_white/arrow_dropdown.svg @@ -1,9 +1,9 @@ - - - - - - - - + + + + + + + + diff --git a/src/resources/themes/v_white/close.svg b/src/resources/themes/v_white/close.svg new file mode 100644 index 00000000..aa6b81c1 --- /dev/null +++ b/src/resources/themes/v_white/close.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/resources/icons/close_grey.svg b/src/resources/themes/v_white/close_grey.svg similarity index 92% rename from src/resources/icons/close_grey.svg rename to src/resources/themes/v_white/close_grey.svg index 9f101e1d..24bddd4e 100644 --- a/src/resources/icons/close_grey.svg +++ b/src/resources/themes/v_white/close_grey.svg @@ -1,10 +1,10 @@ - - - - - - + + + + + + diff --git a/src/resources/icons/float.svg b/src/resources/themes/v_white/float.svg similarity index 98% rename from src/resources/icons/float.svg rename to src/resources/themes/v_white/float.svg index 50029f9a..3c29db9a 100644 --- a/src/resources/icons/float.svg +++ b/src/resources/themes/v_white/float.svg @@ -1,12 +1,12 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/src/resources/themes/v_white/v_white.palette b/src/resources/themes/v_white/v_white.palette new file mode 100644 index 00000000..18755b58 --- /dev/null +++ b/src/resources/themes/v_white/v_white.palette @@ -0,0 +1,171 @@ +; File path could be absolute path or relative path (related to this file). +; Use @color_tag to reference a style. + +[metadata] +qss_file=v_white.qss + +[phony] +; Abstract color attributes. +base_fg=#000000 +base_color_1=#EEEEEE +hover_color_1=#C4C4C4 +selected_color_1=#BEBEBE +base_color_2=#E0E0E0 +separator_color=#E0E0E0 +border_color=#9E9E9E +focus_color=#BDBDBD +content_color=#FAFAFA +selection_color=#64B5F6 + +[widgets] +; Widget color attributes. + +; QWidget. +widget_fg=@base_fg + +; Separator of dock widgets. +dock_separator_bg=@base_color_2 +dock_separator_hover_bg=@base_color_2 + +; Menubar. +menubar_bg=@base_color_1 +menubar_fg=@base_fg +menubar_item_selected_bg=@selected_color_1 + +; Menu. +menu_bg=@base_color_1 +menu_fg=@base_fg +menu_item_selected_bg=@selected_color_1 +menu_separator_bg=@separator_color + +; Tooltip. +tooltip_border=@base_color_2 +tooltip_bg=@base_color_1 +tooltip_fg=@base_fg + +; Toolbar. +toolbar_bg=@base_color_1 +toolbutton_hover_bg=@hover_color_1 +toolbutton_pressed_bg=@selected_color_1 + +; Dockwidget. +dockwidget_title_bg=@base_color_2 +dockwidget_button_hover_bg=@hover_color_1 + +; PushButton. +pushbutton_fg=@base_fg +pushbutton_bg=transparent +pushbutton_border=@border_color +pushbutton_pressed_bg=@selected_color_1 +pushbutton_hover_bg=@hover_color_1 +pushbutton_default_border=#424242 + +pushbutton_cornerbtn_hover_bg=@hover_color_1 +pushbutton_cornerbtn_focus_bg=@focus_color + +pushbutton_statusbtn_hover_bg=@hover_color_1 +pushbutton_statusbtn_focus_bg=@focus_color + +pushbutton_flatbtn_hover_bg=@hover_color_1 +pushbutton_flatbtn_focus_bg=@focus_color + +pushbutton_selectionbtn_hover_bg=@hover_color_1 +pushbutton_selectionbtn_focus_bg=@focus_color + +pushbutton_titlebtn_bg=@base_color_2 +pushbutton_titlebtn_hover_bg=@hover_color_1 +pushbutton_titlebtn_focus_bg=@focus_color + +pushbutton_dangerbtn_fg=#FFF +pushbutton_dangerbtn_border=#D43F3A +pushbutton_dangerbtn_bg=#D9534F +pushbutton_dangerbtn_hover_fg=#FFF +pushbutton_dangerbtn_hover_bg=#C9302C +pushbutton_dangerbtn_hover_border=#AC2925 + +; ComboBox. +combobox_border=@border_color +combobox_fg=@base_fg +combobox_bg=@content_color +combobox_view_border=@border_color +combobox_view_selected_bg=@selected_color_1 +combobox_view_selected_fg=@base_fg +combobox_view_item_hover_fg=@base_fg +combobox_view_item_hover_bg=@hover_color_1 +combobox_focus_bg=@focus_color + +; Label. +label_fg=@base_fg +label_titlelabel_fg=@base_fg +label_titlelabel_bg=@base_color_2 + +; LineEdit. +lineedit_border=@border_color +lineedit_fg=@base_fg +lineedit_bg=@content_color +lineedit_selection_fg=@base_fg +lineedit_selection_bg=@selection_color + +; TabWidget. +tabwidget_pane_border=@selected_color_1 + +; TabBar. +tabbar_fg=@base_fg +tabbar_bg=@base_color_1 +tabbar_border=@border_color + +tabbar_selected_fg=@base_fg +tabbar_selected_bg=@selected_color_1 +tabbar_selected_border=@border_color + +tabbar_hover_fg=@base_fg +tabbar_hover_bg=@hover_color_1 +tabbar_hover_border=@border_color + +tabbar_closebutton_hover_bg=@hover_color_1 +tabbar_clostbutton_focus_bg=@focus_color + +; SelectorItem. +selectoritem_border=@base_fg +selectoritem_fg=@base_fg +selectoritem_bg=@base_color_1 + +; InsertSelector. +insertselector_bg=@base_color_1 + +; TreeView. +treeview_fg=@base_fg +treeview_bg=@content_color +treeview_item_hover_fg=@base_fg +treeview_item_hover_bg=@hover_color_1 +treeview_item_selected_fg=@base_fg +treeview_item_selected_bg=@selected_color_1 +treeview_item_selected_avtive_fg=@base_fg +treeview_item_selected_avtive_bg=@selected_color_1 +treeview_item_selected_inactive_fg=@base_fg +treeview_item_selected_inactive_bg=#D0D0D0 + +; ListView. +listview_fg=@base_fg +listview_bg=@content_color +listview_item_hover_fg=@base_fg +listview_item_hover_bg=@hover_color_1 +listview_item_selected_fg=@base_fg +listview_item_selected_bg=@selected_color_1 +listview_item_selected_avtive_fg=@base_fg +listview_item_selected_avtive_bg=@selected_color_1 +listview_item_selected_inactive_fg=@base_fg +listview_item_selected_inactive_bg=#D0D0D0 + +; Splitter. +splitter_handle_bg=@base_color_2 +splitter_mainsplitter_border=@base_color_2 + +; StatusBar. +statusbar_fg=@base_fg +statusbar_bg=@base_color_1 + +; ScrollBar. +scrollbar_bg=#EEEEEE +scrollbar_handle_bg=#D0D0D0 +scrollbar_addline_bg=#E0E0E0 diff --git a/src/resources/themes/v_white/v_white.qss b/src/resources/themes/v_white/v_white.qss new file mode 100644 index 00000000..d28a6df2 --- /dev/null +++ b/src/resources/themes/v_white/v_white.qss @@ -0,0 +1,561 @@ +QToolTip +{ + border: 1px solid @tooltip_border; + background: @tooltip_bg; + color: @tooltip_fg; + padding: 5px; +} + +/* QWidget */ +QWidget +{ + color: @widget_fg; +} + +QWidget[NotebookPanel="true"] { + padding-left: 3px; +} +/* End QWidget */ + +QMainWindow::separator { + background: @dock_separator_bg; + width: 2px; + height: 2px; +} + +QMainWindow::separator:hover { + background: @dock_separator_hover_bg; +} + +QMenuBar { + border: none; + background: @menubar_bg; + color: @menubar_fg; +} + +QMenuBar::item:selected { + background: @menubar_item_selected_bg; +} + +QMenu { + background: @menu_bg; + color: @menu_fg; + margin: 2px; +} + +QMenu::icon { + margin: 5px; +} + +QMenu::item { + padding: 5px 30px 5px 30px; + background: 1px solid transparent; +} + +QMenu::item:selected { + background: @menu_item_selected_bg; +} + +QMenu::icon:checked { /* appearance of a 'checked' icon */ + border: 2px solid @menu_item_selected_bg; +} + +QMenu::separator { + height: 2px; + background: @menu_separator_bg; + margin-left: 10px; + margin-right: 5px; +} + +QMenu::indicator { + width: 20px; + height: 20px; +} + +QToolBar { + border: none; + background: @toolbar_bg; +} + +QToolButton { + border: none; + background: transparent; + margin: 1px 3px 1px 3px; + padding: 0px; +} + +QToolButton[popupMode="1"] { /* only for MenuButtonPopup */ + padding-right: 16px; /* make way for the popup button */ +} + +QToolButton[popupMode="2"] { /* only for InstantPopup */ + padding-right: 10px; /* make way for the popup button */ +} + +QToolButton:hover { + border:none; + background: @toolbutton_hover_bg; +} + +QToolButton::pressed { + background: @toolbutton_pressed_bg; +} + +/* the subcontrols below are used only in the MenuButtonPopup mode */ +QToolButton::menu-button { + border: none; + width: 16px; +} + +/* DockWidget */ +QDockWidget { + titlebar-close-icon: url(close.svg); + titlebar-normal-icon: url(float.svg); +} + +QDockWidget::Title { + background: @dockwidget_title_bg; + text-align: center left; +} + +QDockWidget::close-button, QDockWidget::float-button { + border: none; +} + +QDockWidget::close-button:hover, QDockWidget::float-button:hover { + background: @dockwidget_button_hover_bg; +} +/* End DockWidget */ + +/* QPushButton */ +QPushButton { + color: @pushbutton_fg; + background: @pushbutton_bg; + border: 1px solid @pushbutton_border; + padding: 3px; + min-width: 80px; +} + +QPushButton:pressed { + background-color: @pushbutton_pressed_bg; +} + +QPushButton:hover { + background-color: @pushbutton_hover_bg; +} + +QPushButton:flat { + border: none; +} + +QPushButton::default { + border-color: @pushbutton_default_border; +} + +QPushButton[CornerBtn="true"] { + padding: 4px -2px 4px -2px; + margin: 0px; + border: none; + background-color: transparent; + min-width: -1; +} + +QPushButton[CornerBtn="true"]::menu-indicator { + image: none; +} + +QPushButton[CornerBtn="true"]:hover { + background-color: @pushbutton_cornerbtn_hover_bg; +} + +QPushButton[CornerBtn="true"]:focus { + background-color: @pushbutton_cornerbtn_focus_bg; +} + +QPushButton[StatusBtn="true"] { + font: bold; + padding: 0px 2px 0px 2px; + margin: 0px; + border: none; + background-color: transparent; + min-width: -1; +} + +QPushButton[StatusBtn="true"]:hover { + background-color: @pushbutton_statusbtn_hover_bg; +} + +QPushButton[StatusBtn="true"]:focus { + background-color: @pushbutton_statusbtn_focus_bg;; +} + +QPushButton[FlatBtn="true"] { + padding: 4px; + margin: 0px; + border: none; + background-color: transparent; + min-width: -1; +} + +QPushButton[FlatBtn="true"]:hover { + background-color: @pushbutton_flatbtn_hover_bg; +} + +QPushButton[FlatBtn="true"]:focus { + background-color: @pushbutton_flatbtn_focus_bg; +} + +QPushButton[SelectionBtn="true"] { + padding: 4px 10px 4px 10px; + border: none; + background-color: transparent; + font-size: 15pt; + text-align: left; + min-width: -1; +} + +QPushButton[SelectionBtn="true"]:hover { + background-color: @pushbutton_selectionbtn_hover_bg; +} + +QPushButton[SelectionBtn="true"]:focus { + background-color: @pushbutton_selectionbtn_focus_bg; +} + +QPushButton[TitleBtn="true"] { + padding: 4px; + margin: 0px; + border: none; + background-color: @pushbutton_titlebtn_bg; + min-width: -1; +} + +QPushButton[TitleBtn="true"]:hover { + background-color: @pushbutton_titlebtn_hover_bg; +} + +QPushButton[TitleBtn="true"]:focus { + background-color: @pushbutton_titlebtn_focus_bg; +} + +QPushButton[DangerBtn="true"] { + color: @pushbutton_dangerbtn_fg; + border-color: @pushbutton_dangerbtn_border; + background-color: @pushbutton_dangerbtn_bg; + min-width: -1; +} + +QPushButton[DangerBtn="true"]:hover { + color: @pushbutton_dangerbtn_hover_fg; + border-color: @pushbutton_dangerbtn_hover_border; + background-color: @pushbutton_dangerbtn_hover_bg; +} +/* End QPushButton*/ + +/* QComboBox */ +QComboBox { + padding: 3px; + color: @combobox_fg; + background: @combobox_bg; + border: 1px solid @combobox_border; +} + +QComboBox:focus { + background-color: @combobox_focus_bg; +} + +QComboBox::drop-down { + subcontrol-origin: padding; + subcontrol-position: top right; + width: 20px; + border: none; + background: transparent; +} + +QComboBox::down-arrow { + image: url(arrow_dropdown.svg); + width: 20px; + height: 20px; +} + +QComboBox QAbstractItemView { + padding: 2px; + border: 1px solid @combobox_view_border; + background: @combobox_bg; + selection-color: @combobox_view_selected_fg; + selection-background-color: @combobox_view_selected_bg; +} + +QComboBox QAbstractItemView::item { + background: transparent; + padding: 3px; +} + +QComboBox QAbstractItemView::item:hover { + color: @combobox_view_item_hover_fg; + background: @combobox_view_item_hover_bg; +} + +QComboBox#NotebookSelector { + border: none; + font-size: 13pt; + padding-top: 3px; + padding-bottom: 3px; + icon-size: 30px; +} + +QComboBox#NotebookSelector QListWidget { + border: 1px solid @combobox_view_border; + background-color: @combobox_bg; + font-size: 13pt; + icon-size: 30px; +} + +QComboBox#NotebookSelector QListWidget::item { + padding-top: 10px; + padding-bottom: 10px; +} + +QComboBox#NotebookSelector QListWidget::item:hover { + color: @combobox_view_item_hover_fg; + background-color: @combobox_view_item_hover_bg; +} +/* End QComboBox */ + +/* QLabel */ +QLabel { + border: none; + color: @label_fg; + background: transparent; +} + +QLabel[TitleLabel="true"] { + padding-top: 5px; + padding-bottom: 5px; + color: @label_titlelabel_fg; + background-color: @label_titlelabel_bg; +} + +QLabel[ColorRedLabel="true"] { + padding-left: 5px; + padding-right: 5px; + font: bold; + color: white; + border-radius: 2px; + background-color: #D32F2F; +} + +QLabel[ColorGreenLabel="true"] { + padding-left: 5px; + padding-right: 5px; + font: bold; + color: white; + border-radius: 2px; + background-color: #388E3C; +} + +QLabel[ColorGreyLabel="true"] { + padding-left: 5px; + padding-right: 5px; + font: bold; + color: white; + border-radius: 2px; + background-color: #616161; +} + +QLabel[ColorTealLabel="true"] { + padding-left: 5px; + padding-right: 5px; + font: bold; + color: white; + border-radius: 2px; + background-color: #00796B; +} +/* End QLabel */ + +/* QLineEdit */ +QLineEdit { + border: 1px solid @lineedit_border; + padding: 3px; + color: @lineedit_fg; + background: @lineedit_bg; + selection-color: @lineedit_selection_fg; + selection-background-color: @lineedit_selection_bg; +} + +QLineEdit[VimCommandLine="true"] { + padding: 0px; + margin: 0px; + border: none; +} +/* End QLineEdit */ + +/* QTabWidget */ +QTabWidget { + border: none; +} + +QTabWidget::pane { + border: none; + border-top: 3px solid @tabwidget_pane_border; +} +/* End QTabWidget */ + +/* QTabBar */ +QTabBar::tab { + color: @tabbar_fg; + background: @tabbar_bg; + border: 1px solid @tabbar_border; + border-bottom: none; + padding: 2px; +} + +QTabBar::tab:selected { + color: @tabbar_selected_fg; + background: @tabbar_selected_bg; + border: 1px solid @tabbar_selected_border; + border-bottom: none; +} + +QTabBar::tab:hover { + color: @tabbar_hover_fg; + background: @tabbar_hover_bg; + border: 1px solid @tabbar_hover_border; + border-bottom: none; +} + +QTabBar::tab:!selected { + margin-top: 2px; /* make non-selected tabs look smaller */ +} + +QTabBar::close-button { + image: url(close_grey.svg); +} + +QTabBar::close-button:hover { + image: url(close.svg); + background-color: @tabbar_closebutton_hover_bg; +} + +QTabBar::close-button:focus { + image: url(close.svg); + background-color: @tabbar_clostbutton_focus_bg; +} +/* End QTabBar */ + +VSelectorItemWidget QLabel[SelectorItemShortcutLabel="true"] { + font: bold; + border: 2px solid @selectoritem_border; + padding: 3px; + border-radius: 5px; + background-color: @selectoritem_bg; + color: @selectoritem_fg; +} + +VInsertSelector { + border: none; + background: @insertselector_bg; +} + +/* QTreeView */ +QTreeView { + color: @treeview_fg; + background: @treeview_bg; + show-decoration-selected: 1; + padding-top: 3px; + border: none; +} + +QTreeView::item { + padding-top: 5px; + padding-bottom: 5px; +} + +QTreeView::item:hover { + color: @treeview_item_hover_fg; + background: @treeview_item_hover_bg; +} + +QTreeView::item:selected { + color: @treeview_item_selected_fg; + background: @treeview_item_selected_bg; +} + +QTreeView::item:selected:active { + color: @treeview_item_selected_active_fg; + background: @treeview_item_selected_active_bg; +} + +QTreeView::item:selected:!active { + color: @treeview_item_selected_inactive_fg; + background: @treeview_item_selected_inactive_bg; +} +/* End QTreeView */ + +/* QListView */ +QListView { + color: @listview_fg; + background: @listview_bg; + show-decoration-selected: 1; + padding-top: 3px; + border: none; +} + +QListView::item { + padding-top: 5px; + padding-bottom: 5px; +} + +QListView::item:hover { + color: @listview_item_hover_fg; + background: @listview_item_hover_bg; +} + +QListView::item:selected { + color: @listview_item_selected_fg; + background: @listview_item_selected_bg; +} + +QListView::item:selected:active { + color: @listview_item_selected_active_fg; + background: @listview_item_selected_active_bg; +} + +QListView::item:selected:!active { + color: @listview_item_selected_inactive_fg; + background: @listview_item_selected_inactive_bg; +} +/* End QListView */ + +/* QSplitter */ +QSplitter { + border: none; +} + +QSplitter::handle { + background-color: @splitter_handle_bg; +} + +QSplitter::handle:vertical { + height: 2px; +} + +QSplitter::handle:horizontal { + width: 2px; +} + +QSplitter#MainSplitter { + border-top: 2px solid @splitter_mainsplitter_border; +} +/* End QSplitter */ + +/* QStatusBar */ +QStatusBar { + color: @statusbar_fg; + background: @statusbar_bg; +} +/* End QStatusBar */ + +QWidget[MainEditor="true"] { + border: none; +} diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index e1ff5ae0..b2fb2938 100644 --- a/src/resources/vnote.ini +++ b/src/resources/vnote.ini @@ -1,4 +1,7 @@ [global] +; Theme name +theme=v_white + welcome_page_path=:/resources/welcome.html ; CSS style for Markdown template diff --git a/src/src.pro b/src/src.pro index b3a7f787..2256f2ad 100644 --- a/src/src.pro +++ b/src/src.pro @@ -98,7 +98,8 @@ SOURCES += main.cpp\ utils/vimnavigationforwidget.cpp \ vtoolbox.cpp \ vinsertselector.cpp \ - utils/vclipboardutils.cpp + utils/vclipboardutils.cpp \ + vpalette.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -183,7 +184,8 @@ HEADERS += vmainwindow.h \ utils/vimnavigationforwidget.h \ vtoolbox.h \ vinsertselector.h \ - utils/vclipboardutils.h + utils/vclipboardutils.h \ + vpalette.h RESOURCES += \ vnote.qrc \ diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index e21aec6f..20a428c1 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "vorphanfile.h" #include "vnote.h" @@ -1074,3 +1076,12 @@ bool VUtils::isMetaKey(int p_key) || p_key == Qt::Key_Meta || p_key == Qt::Key_Alt; } + +QComboBox *VUtils::getComboBox(QWidget *p_parent) +{ + QComboBox *box = new QComboBox(p_parent); + QStyledItemDelegate *itemDelegate = new QStyledItemDelegate(box); + box->setItemDelegate(itemDelegate); + + return box; +} diff --git a/src/utils/vutils.h b/src/utils/vutils.h index fab8100d..3f89f177 100644 --- a/src/utils/vutils.h +++ b/src/utils/vutils.h @@ -15,6 +15,8 @@ class QKeyEvent; class VFile; class VOrphanFile; class VNotebook; +class QWidget; +class QComboBox; #if !defined(V_ASSERT) #define V_ASSERT(cond) ((!(cond)) ? qt_assert(#cond, __FILE__, __LINE__) : qt_noop()) @@ -261,6 +263,9 @@ public: // Ctrl, Meta, Shift, Alt. static bool isMetaKey(int p_key); + // Create and return a QComboBox. + static QComboBox *getComboBox(QWidget *p_parent = nullptr); + // Regular expression for image link. // ![image title]( http://github.com/tamlok/vnote.jpg "alt \" text" ) // Captured texts (need to be trimmed): diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index 2c23cc43..84bc20e8 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -32,6 +32,8 @@ const QString VConfigManager::c_snippetConfigFile = QString("snippet.json"); const QString VConfigManager::c_styleConfigFolder = QString("styles"); +const QString VConfigManager::c_themeConfigFolder = QString("themes"); + const QString VConfigManager::c_codeBlockStyleConfigFolder = QString("codeblock_styles"); const QString VConfigManager::c_templateConfigFolder = QString("templates"); @@ -74,6 +76,8 @@ void VConfigManager::initialize() outputDefaultCodeBlockCssStyle(); outputDefaultEditorStyle(); + initThemes(); + m_defaultEditPalette = QTextEdit().palette(); m_editorStyle = getConfigFromSettings("global", "editor_style").toString(); @@ -283,6 +287,9 @@ void VConfigManager::initialize() m_vimExemptionKeys = getConfigFromSettings("global", "vim_exemption_keys").toString(); + + m_theme = getConfigFromSettings("global", + "theme").toString(); } void VConfigManager::initSettings() @@ -783,36 +790,53 @@ QString VConfigManager::getConfigFilePath() const return userSettings->fileName(); } -QString VConfigManager::getStyleConfigFolder() const +const QString &VConfigManager::getStyleConfigFolder() const { static QString path = QDir(getConfigFolder()).filePath(c_styleConfigFolder); return path; } -QString VConfigManager::getCodeBlockStyleConfigFolder() const +const QString &VConfigManager::getThemeConfigFolder() const +{ + static QString path = QDir(getConfigFolder()).filePath(c_themeConfigFolder); + return path; +} + +const QString &VConfigManager::getCodeBlockStyleConfigFolder() const { static QString path = QDir(getStyleConfigFolder()).filePath(c_codeBlockStyleConfigFolder); return path; } -QString VConfigManager::getTemplateConfigFolder() const +const QString &VConfigManager::getTemplateConfigFolder() const { static QString path = QDir(getConfigFolder()).filePath(c_templateConfigFolder); return path; } -QString VConfigManager::getSnippetConfigFolder() const +const QString &VConfigManager::getSnippetConfigFolder() const { static QString path = QDir(getConfigFolder()).filePath(c_snippetConfigFolder); return path; } -QString VConfigManager::getSnippetConfigFilePath() const +const QString &VConfigManager::getSnippetConfigFilePath() const { static QString path = QDir(getSnippetConfigFolder()).filePath(c_snippetConfigFile); return path; } +QString VConfigManager::getThemeFile() const +{ + QString file; + auto it = m_themes.find(m_theme); + if (it != m_themes.end()) { + file = QDir(getThemeConfigFolder()).filePath(it.value()); + } + + return file; +} + QVector VConfigManager::getCssStyles() const { QVector res; @@ -1368,3 +1392,31 @@ const QString &VConfigManager::getFlashPage() const return m_flashPage; } + +void VConfigManager::initThemes() +{ + m_themes.clear(); + + // Built-in. + m_themes.insert(tr("v_white"), ":/resources/themes/v_white/v_white.palette"); + + // User theme folder. + QDir dir(getThemeConfigFolder()); + if (!dir.exists()) { + dir.mkpath(getThemeConfigFolder()); + return; + } + + dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks); + QStringList dirs = dir.entryList(); + for (auto const &item : dirs) { + QDir themeDir(dir.filePath(item)); + QStringList files = themeDir.entryList(QStringList() << "*.palette"); + if (files.size() != 1) { + continue; + } + + QFileInfo fi(files[0]); + m_themes.insert(fi.completeBaseName(), themeDir.filePath(files[0])); + } +} diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index a711542d..ed6a9430 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -353,15 +353,20 @@ public: QString getConfigFilePath() const; // Get the folder c_styleConfigFolder in the config folder. - QString getStyleConfigFolder() const; + const QString &getStyleConfigFolder() const; // Get the folder c_templateConfigFolder in the config folder. - QString getTemplateConfigFolder() const; + const QString &getTemplateConfigFolder() const; + + // Get the folder c_themeConfigFolder in the config folder. + const QString &getThemeConfigFolder() const; // Get the folder c_snippetConfigFolder in the config folder. - QString getSnippetConfigFolder() const; + const QString &getSnippetConfigFolder() const; - QString getSnippetConfigFilePath() const; + const QString &getSnippetConfigFilePath() const; + + QString getThemeFile() const; // Read all available css files in c_styleConfigFolder. QVector getCssStyles() const; @@ -370,7 +375,7 @@ public: QVector getNoteTemplates(DocType p_type = DocType::Unknown) const; // Get the folder c_codeBlockStyleConfigFolder in the config folder. - QString getCodeBlockStyleConfigFolder() const; + const QString &getCodeBlockStyleConfigFolder() const; // Read all available css files in c_codeBlockStyleConfigFolder. QVector getCodeBlockCssStyles() const; @@ -397,6 +402,14 @@ public: const QString &getFlashPage() const; + // All the themes. + QList getThemes() const; + + // Return current theme name. + const QString &getTheme() const; + + void setTheme(const QString &p_theme); + private: // Look up a config from user and default settings. QVariant getConfigFromSettings(const QString §ion, const QString &key) const; @@ -474,6 +487,9 @@ private: // Whether @p_seq is a valid key sequence for shortcuts. bool isValidKeySequence(const QString &p_seq); + // Init the themes name-file mappings. + void initThemes(); + // Default font and palette. QFont m_defaultEditFont; QPalette m_defaultEditPalette; @@ -760,6 +776,13 @@ private: // Absolute path of flash page. QString m_flashPage; + // The theme name. + QString m_theme; + + // All the themes. + // [name] -> [file path]. + QMap m_themes; + // The name of the config file in each directory, obsolete. // Use c_dirConfigFile instead. static const QString c_obsoleteDirConfigFile; @@ -792,6 +815,9 @@ private: // The folder name of style files. static const QString c_styleConfigFolder; + // The folder name of theme files. + static const QString c_themeConfigFolder; + // The folder name of code block style files. static const QString c_codeBlockStyleConfigFolder; @@ -1881,4 +1907,24 @@ inline const QString &VConfigManager::getVimExemptionKeys() const return m_vimExemptionKeys; } +inline QList VConfigManager::getThemes() const +{ + return m_themes.keys(); +} + +inline const QString &VConfigManager::getTheme() const +{ + return m_theme; +} + +inline void VConfigManager::setTheme(const QString &p_theme) +{ + if (p_theme == m_theme) { + return; + } + + m_theme = p_theme; + setConfigToSettings("global", "theme", m_theme); +} + #endif // VCONFIGMANAGER_H diff --git a/src/vinsertselector.cpp b/src/vinsertselector.cpp index 4bc5e766..53860bf1 100644 --- a/src/vinsertselector.cpp +++ b/src/vinsertselector.cpp @@ -111,3 +111,13 @@ void VInsertSelector::showEvent(QShowEvent *p_event) setFocus(); } } + +void VInsertSelector::paintEvent(QPaintEvent *p_event) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + + QWidget::paintEvent(p_event); +} diff --git a/src/vinsertselector.h b/src/vinsertselector.h index 04988199..ee04fa36 100644 --- a/src/vinsertselector.h +++ b/src/vinsertselector.h @@ -7,6 +7,7 @@ class QPushButton; class QKeyEvent; class QShowEvent; +class QPaintEvent; struct VInsertSelectorItem { @@ -64,6 +65,8 @@ protected: void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent *p_event) Q_DECL_OVERRIDE; + private slots: void itemClicked(const QString &p_name); diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 7b87496d..f667fa83 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -925,6 +925,9 @@ void VMainWindow::initFileMenu() this, &VMainWindow::printNote); m_printAct->setEnabled(false); + // Themes. + initThemeMenu(fileMenu); + // Settings. QAction *settingsAct = newAction(QIcon(":/resources/icons/settings.svg"), tr("&Settings"), this); @@ -2775,3 +2778,36 @@ void VMainWindow::initHeadingButton(QToolBar *p_tb) m_headingBtn->setMenu(menu); p_tb->addWidget(m_headingBtn); } + +void VMainWindow::initThemeMenu(QMenu *p_menu) +{ + QMenu *themeMenu = p_menu->addMenu(tr("Theme")); + themeMenu->setToolTipsVisible(true); + + QActionGroup *ag = new QActionGroup(this); + connect(ag, &QActionGroup::triggered, + this, [this](QAction *p_action) { + if (!p_action) { + return; + } + + QString data = p_action->data().toString(); + g_config->setTheme(data); + }); + + QList themes = g_config->getThemes(); + QString theme = g_config->getTheme(); + for (auto const &item : themes) { + QAction *act = new QAction(item, ag); + act->setToolTip(tr("Set as the theme of VNote (restart VNote to make it work)")); + act->setCheckable(true); + act->setData(item); + + // Add it to the menu. + themeMenu->addAction(act); + + if (theme == item) { + act->setChecked(true); + } + } +} diff --git a/src/vmainwindow.h b/src/vmainwindow.h index df5d2f5c..4b2939a3 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -263,6 +263,8 @@ private: void initHeadingButton(QToolBar *p_tb); + void initThemeMenu(QMenu *p_emnu); + // Captain mode functions. // Popup the attachment list if it is enabled. diff --git a/src/vmdtab.cpp b/src/vmdtab.cpp index 47628c65..0b1cdd32 100644 --- a/src/vmdtab.cpp +++ b/src/vmdtab.cpp @@ -413,6 +413,7 @@ void VMdTab::setupMarkdownEditor() Q_ASSERT(!m_editor); m_editor = new VMdEditor(m_file, m_document, m_mdConType, this); + m_editor->setProperty("MainEditor", true); connect(m_editor, &VMdEditor::headersChanged, this, &VMdTab::updateOutlineFromHeaders); connect(m_editor, SIGNAL(currentHeaderChanged(int)), diff --git a/src/vnote.qrc b/src/vnote.qrc index 0264e946..c98bd4c4 100644 --- a/src/vnote.qrc +++ b/src/vnote.qrc @@ -65,7 +65,6 @@ resources/icons/paste.svg resources/icons/dir_item.svg resources/icons/notebook_item.svg - resources/icons/arrow_dropdown.svg resources/icons/vnote.png resources/icons/insert_image.svg resources/icons/import_note.svg @@ -93,8 +92,6 @@ utils/mermaid/mermaid.forest.css utils/mermaid/mermaidAPI.min.js resources/icons/close_red.svg - resources/icons/close_grey.svg - resources/icons/float.svg resources/docs/shortcuts_en.md resources/docs/shortcuts_zh.md resources/styles/default.css @@ -107,7 +104,6 @@ utils/markdown-it/markdown-it-sub.min.js utils/markdown-it/markdown-it-sup.min.js utils/markdown-it/markdown-it-footnote.min.js - resources/icons/arrow_dropup.svg resources/icons/vnote_update.svg utils/flowchart.js/flowchart.min.js utils/flowchart.js/raphael.min.js @@ -144,5 +140,11 @@ resources/icons/reading_modified.svg resources/icons/flash_page.svg resources/icons/heading.svg + resources/themes/v_white/arrow_dropdown.svg + resources/themes/v_white/close.svg + resources/themes/v_white/close_grey.svg + resources/themes/v_white/float.svg + resources/themes/v_white/v_white.palette + resources/themes/v_white/v_white.qss diff --git a/src/vpalette.cpp b/src/vpalette.cpp new file mode 100644 index 00000000..13730c8f --- /dev/null +++ b/src/vpalette.cpp @@ -0,0 +1,129 @@ +#include "vpalette.h" + +#include +#include +#include +#include +#include + +#include "utils/vutils.h" + +VPalette::VPalette(const QString &p_file) +{ + init(p_file); +} + +void VPalette::init(const QString &p_file) +{ + m_file = QFileInfo(p_file).absoluteFilePath(); + + QSettings settings(p_file, QSettings::IniFormat); + initMetaData(&settings, "metadata"); + initPaleteFromSettings(&settings, "phony"); + initPaleteFromSettings(&settings, "widgets"); +} + +void VPalette::initMetaData(QSettings *p_settings, const QString &p_group) +{ + p_settings->beginGroup(p_group); + // Qss file. + QString val = p_settings->value("qss_file").toString(); + if (!val.isEmpty()) { + m_qssFile = QDir(VUtils::basePathFromPath(m_file)).filePath(val); + qDebug() << "theme file" << m_file << "qss file" << m_qssFile; + } + + p_settings->endGroup(); +} + +void VPalette::initPaleteFromSettings(QSettings *p_settings, const QString &p_group) +{ + QRegExp reg("@(\\w+)"); + + p_settings->beginGroup(p_group); + QStringList keys = p_settings->childKeys(); + for (auto const & key : keys) { + if (key.isEmpty()) { + continue; + } + + QString val = p_settings->value(key).toString(); + if (reg.exactMatch(val)) { + auto it = m_palette.find(reg.cap(1)); + if (it != m_palette.end()) { + val = it.value(); + } else { + qWarning() << "non-defined reference attribute" << key << "in palette" << p_settings->fileName(); + val.clear(); + } + } + + m_palette.insert(key, val); + } + + p_settings->endGroup(); +} + +QString VPalette::color(const QString &p_name) const +{ + auto it = m_palette.find(p_name); + if (it != m_palette.end()) { + return it.value(); + } + + return QString(); +} + +void VPalette::fillStyle(QString &p_style) const +{ + // Cap(2) is the string to be replaced. + QRegExp reg("(\\s|:)@(\\w+)(?=\\W)"); + + int pos = 0; + while (pos < p_style.size()) { + int idx = p_style.indexOf(reg, pos); + if (idx == -1) { + break; + } + + QString name = reg.cap(2); + QString val = color(name); + + if (val.isEmpty()) { + pos = idx + reg.matchedLength(); + } else { + pos = idx + reg.matchedLength() + val.size() - name.size() - 1; + p_style.replace(idx + reg.cap(1).size(), name.size() + 1, val); + } + } +} + +QString VPalette::fetchQtStyleSheet() const +{ + QString style = VUtils::readFileFromDisk(m_qssFile); + fillStyle(style); + fillAbsoluteUrl(style); + + return style; +} + +void VPalette::fillAbsoluteUrl(QString &p_style) const +{ + // Cap(2) is the string to be replaced. + QRegExp reg("(\\s|:)url\\(([^\\(\\)]+)\\)(?=\\W)"); + int literalSize = QString("url(").size(); + QDir dir(VUtils::basePathFromPath(m_file)); + + int pos = 0; + while (pos < p_style.size()) { + int idx = p_style.indexOf(reg, pos); + if (idx == -1) { + break; + } + + QString url = reg.cap(2); + QString abUrl = dir.filePath(url); + pos = idx + reg.matchedLength() + abUrl.size() - url.size(); + p_style.replace(idx + reg.cap(1).size() + literalSize, url.size(), abUrl); + } +} diff --git a/src/vpalette.h b/src/vpalette.h new file mode 100644 index 00000000..164b038e --- /dev/null +++ b/src/vpalette.h @@ -0,0 +1,39 @@ +#ifndef VPALETTE_H +#define VPALETTE_H + +#include +#include + +class QSettings; + + +class VPalette +{ +public: + explicit VPalette(const QString &p_file); + + QString color(const QString &p_name) const; + + // Read QSS file. + QString fetchQtStyleSheet() const; + +private: + void init(const QString &p_file); + + void initPaleteFromSettings(QSettings *p_settings, const QString &p_group); + + void initMetaData(QSettings *p_settings, const QString &p_group); + + void fillStyle(QString &p_style) const; + + void fillAbsoluteUrl(QString &p_style) const; + + // File path of the palette file. + QString m_file; + + QHash m_palette; + + QString m_qssFile; +}; + +#endif // VPALETTE_H