From 05885bad617fcd9b9c9ef3c72069b497b0b28592 Mon Sep 17 00:00:00 2001 From: John Gehrig Date: Sat, 9 May 2020 01:30:18 -0400 Subject: [PATCH] Add GuiAdaptive{Color,Font,Style,StyleList} Configures Qt to use Neovim font/color settings. `:GuiAdaptiveColor 1` will paint Qt Widgets with the neovim foreground/background colors. `:GuiAdaptiveFont 1` will set Qt font styling to the active neovim font. `GuiAdaptiveStyle {name}` will override the Qt style. `GuiAdaptiveStyleList {name}` will print all installed Qt Style names --- src/gui/mainwindow.cpp | 143 ++++++++++++++++++++++- src/gui/mainwindow.h | 19 ++- src/gui/runtime/doc/nvim_gui_shim.txt | 20 +++- src/gui/runtime/plugin/nvim_gui_shim.vim | 24 ++++ src/gui/shell.cpp | 68 +++++++++++ src/gui/shell.h | 36 ++++++ 6 files changed, 304 insertions(+), 6 deletions(-) diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 286a17466..1f0ddb49c 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace NeovimQt { @@ -10,6 +11,8 @@ namespace NeovimQt { MainWindow::MainWindow(NeovimConnector* c, ShellOptions opts, QWidget* parent) : QMainWindow(parent) , m_shell_options(opts) + , m_defaultFont{ font() } + , m_defaultPalette{ palette() } { m_errorWidget = new ErrorWidget(); m_stack.addWidget(m_errorWidget); @@ -124,6 +127,19 @@ void MainWindow::init(NeovimConnector *c) this, &MainWindow::neovimSendPaste); connect(m_actSelectAll, &QAction::triggered, this, &MainWindow::neovimSendSelectAll); + + // GuiAdaptive Color/Font/Style Signal/Slot Connections + connect(m_shell, &Shell::setGuiAdaptiveColorEnabled, + this, &MainWindow::setGuiAdaptiveColorEnabled); + connect(m_shell, &Shell::setGuiAdaptiveFontEnabled, + this, &MainWindow::setGuiAdaptiveFontEnabled); + connect(m_shell, &Shell::setGuiAdaptiveStyle, + this, &MainWindow::setGuiAdaptiveStyle); + connect(m_shell, &Shell::showGuiAdaptiveStyleList, + this, &MainWindow::showGuiAdaptiveStyleList); + connect(m_shell, &Shell::colorsChanged, + this, &MainWindow::updateAdaptiveColor); + m_shell->setFocus(Qt::OtherFocusReason); if (m_nvim->errorCause()) { @@ -416,4 +432,129 @@ void MainWindow::restoreWindowGeometry() restoreState(settings.value("window_state").toByteArray()); } -} // namespace NeovimQt +void MainWindow::setGuiAdaptiveColorEnabled(bool isEnabled) +{ + m_isAdaptiveColorEnabled = isEnabled; + + updateAdaptiveColor(); +} + +void MainWindow::setGuiAdaptiveFontEnabled(bool isEnabled) +{ + m_isAdaptiveFontEnabled = isEnabled; + + updateAdaptiveFont(); +} + +void MainWindow::setGuiAdaptiveStyle(const QString& styleName) +{ + // The style may be empty if the name is invalid. This appears to be safe, + // calling setStyle(nullptr) will restore the default Qt Style. + QStyle* pStyle{ QStyleFactory::create(styleName) }; + + auto childrenWidgets{ findChildren() + m_shell->findChildren() }; + + for (const auto childWidget : childrenWidgets) { + childWidget->setStyle(pStyle); + } + + setStyle(pStyle); +} + +void MainWindow::showGuiAdaptiveStyleList() +{ + const QString styleKeys{ QStyleFactory::keys().join("\n") }; + QString echoCommand{ R"(echo "%1")" }; + m_nvim->api0()->vim_command(echoCommand.arg(styleKeys).toLatin1()); +} + +void MainWindow::updateAdaptiveFont() noexcept +{ + if (!m_shell) { + return; + } + + const QFont& font{ (m_isAdaptiveFontEnabled) ? + m_shell->font() : m_defaultFont }; + + setFont(font); + + auto childrenWidgets{ findChildren() + m_shell->findChildren() }; + + for (const auto childWidget : childrenWidgets) { + // Do not call setFont() on ShellWidget objects + if (qobject_cast(childWidget)) { + continue; + } + + childWidget->setFont(font); + } +} + +static QPalette CreatePaletteFromHighlightGroups(const Shell& shell) noexcept +{ + const QColor& background{ shell.background() }; + const QColor& foreground{ shell.foreground() }; + + QPalette palette; + palette.setColor(QPalette::Background, background); + palette.setColor(QPalette::Foreground, foreground); + palette.setColor(QPalette::Window, background); + palette.setColor(QPalette::WindowText, foreground); + palette.setColor(QPalette::Base, background); + palette.setColor(QPalette::Text, foreground); + palette.setColor(QPalette::Button, background); + palette.setColor(QPalette::ButtonText, foreground); + + return palette; +} + +void MainWindow::updateAdaptiveColor() noexcept +{ + if (!m_shell) { + return; + } + + auto setPaletteAllChildren = [&](const QPalette& palette) noexcept + { + setPalette(palette); + auto childrenWidgets{ findChildren() + m_shell->findChildren() }; + + for (const auto childWidget : childrenWidgets) { + childWidget->setPalette(palette); + } + }; + + if (!m_isAdaptiveColorEnabled) { + setPaletteAllChildren(m_defaultPalette); + return; + } + + setPaletteAllChildren(CreatePaletteFromHighlightGroups(*m_shell)); + + // Some widgets support specialized palettes + PopupMenu& popupMenu{ m_shell->getPopupMenu() }; + const bool isPopupMenuSupported { m_shell->IsHighlightGroup("Pmenu") + && m_shell->IsHighlightGroup("PmenuSel") }; + if (isPopupMenuSupported) { + QPalette palette; + + const HighlightAttribute pmenu{ m_shell->GetHighlightGroup("Pmenu") }; + palette.setColor(QPalette::Base, pmenu.GetBackgroundColor()); + palette.setColor(QPalette::Text, pmenu.GetForegroundColor()); + + const HighlightAttribute pmenusel{ m_shell->GetHighlightGroup("PmenuSel") }; + palette.setColor(QPalette::Highlight, pmenusel.GetBackgroundColor()); + palette.setColor(QPalette::HighlightedText, pmenusel.GetForegroundColor()); + + auto childrenWidgets{ popupMenu.findChildren() }; + + for (const auto childWidget : childrenWidgets) { + childWidget->setPalette(palette); + } + + popupMenu.setPalette(palette); + } +} + +} // namespace NeovimQt diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h index 166aa7e06..44d2613fd 100644 --- a/src/gui/mainwindow.h +++ b/src/gui/mainwindow.h @@ -2,14 +2,16 @@ #define NEOVIM_QT_MAINWINDOW #include +#include +#include #include #include -#include -#include "treeview.h" -#include "neovimconnector.h" + #include "errorwidget.h" +#include "neovimconnector.h" #include "scrollbar.h" #include "shell.h" +#include "treeview.h" namespace NeovimQt { @@ -59,6 +61,12 @@ private slots: void changeTab(int index); void saveWindowGeometry(); + // GuiAdaptive Color/Font/Style Slots + void setGuiAdaptiveColorEnabled(bool isEnabled); + void setGuiAdaptiveFontEnabled(bool isEnabled); + void setGuiAdaptiveStyle(const QString& style); + void showGuiAdaptiveStyleList(); + private: void init(NeovimConnector *); @@ -81,11 +89,14 @@ private slots: QAction* m_actSelectAll{ nullptr }; ScrollBar* m_scrollbar{ nullptr }; - // GuiAdaptive Color/Font + // GuiAdaptive Color/Font/Style bool m_isAdaptiveColorEnabled{ false }; bool m_isAdaptiveFontEnabled{ false }; QFont m_defaultFont; QPalette m_defaultPalette; + + void updateAdaptiveColor() noexcept; + void updateAdaptiveFont() noexcept; }; } // Namespace diff --git a/src/gui/runtime/doc/nvim_gui_shim.txt b/src/gui/runtime/doc/nvim_gui_shim.txt index 58a536c48..b66a31af5 100644 --- a/src/gui/runtime/doc/nvim_gui_shim.txt +++ b/src/gui/runtime/doc/nvim_gui_shim.txt @@ -17,7 +17,25 @@ can be used in |ginit.vim| provided that ============================================================================== 1. Commands - *Guifont* *GuiFont* + *GuiAdaptiveColor* +GuiAdaptiveColor Use the neovim `colorscheme` to style the GUI. +> + :GuiAdaptiveColor 1 +< + *GuiAdaptiveFont* +GuiAdaptiveFont Use the current `GuiFont`` to decorate the GUI. +> + :GuiAdaptiveFont 1 +< + *GuiAdaptiveStyle* +GuiAdaptiveStyle Override the default Qt Style/Theme. See +> + :GuiAdaptiveStyle Fusion +< + *GuiAdaptiveStyleList* +GuiAdaptiveStyleList Shows the list of available Qt Styles. + + *GuiFont* GuiFont When called with no arguments this command display the current font used by the GUI. diff --git a/src/gui/runtime/plugin/nvim_gui_shim.vim b/src/gui/runtime/plugin/nvim_gui_shim.vim index 1052a25a3..baa2ebe18 100644 --- a/src/gui/runtime/plugin/nvim_gui_shim.vim +++ b/src/gui/runtime/plugin/nvim_gui_shim.vim @@ -232,3 +232,27 @@ function! s:GuiScrollBar(enable) abort endfunction command! -nargs=1 GuiScrollBar call s:GuiScrollBar() + +" Use Neovim theming for Qt Widgets +function! s:GuiAdaptiveColor(enable) abort + call rpcnotify(0, 'Gui', 'AdaptiveColor', a:enable) +endfunction +command! -nargs=1 GuiAdaptiveColor call s:GuiAdaptiveColor() + +" Use Neovim font for Qt Widgets +function! s:GuiAdaptiveFont(enable) abort + call rpcnotify(0, 'Gui', 'AdaptiveFont', a:enable) +endfunction +command! -nargs=1 GuiAdaptiveFont call s:GuiAdaptiveFont() + +" Override default Qt Style using QStyleFactory +function! s:GuiAdaptiveStyle(styleName, ...) abort + call rpcnotify(0, 'Gui', 'AdaptiveStyle', a:styleName) +endfunction +command! -nargs=? GuiAdaptiveStyle call s:GuiAdaptiveStyle("") + +" Print a list of available Qt Styles +function! s:GuiAdaptiveStyleList() abort + call rpcnotify(0, 'Gui', 'AdaptiveStyleList') +endfunction +command! -nargs=0 GuiAdaptiveStyleList call s:GuiAdaptiveStyleList() diff --git a/src/gui/shell.cpp b/src/gui/shell.cpp index e88ac2673..563607248 100644 --- a/src/gui/shell.cpp +++ b/src/gui/shell.cpp @@ -533,6 +533,7 @@ void Shell::handleRedraw(const QByteArray& name, const QVariantList& opargs) } else if (name == "grid_scroll") { handleGridScroll(opargs); } else if (name == "hl_group_set") { + handleHighlightGroupSet(opargs); } else if (name == "win_viewport") { } else { qDebug() << "Received unknown redraw notification" << name << opargs; @@ -818,6 +819,14 @@ void Shell::handleNeovimNotification(const QByteArray &name, const QVariantList& QGuiApplication::clipboard()->setMimeData(clipData, clipboard); } else if (guiEvName == "ShowContextMenu") { emit neovimShowContextMenu(); + } else if (guiEvName == "AdaptiveColor") { + handleGuiAdaptiveColor(args); + } else if (guiEvName == "AdaptiveFont") { + handleGuiAdaptiveFont(args); + } else if (guiEvName == "AdaptiveStyle") { + handleGuiAdaptiveStyle(args); + } else if (guiEvName == "AdaptiveStyleList") { + handleGuiAdaptiveStyleList(); } return; } else if (name != "redraw") { @@ -964,6 +973,7 @@ void Shell::handleDefaultColorsSet(const QVariantList& opargs) // Cells drawn with the default colors require a re-paint update(); + emit colorsChanged(); } void Shell::handleHighlightAttributeDefine(const QVariantList& opargs) @@ -986,6 +996,20 @@ void Shell::handleHighlightAttributeDefine(const QVariantList& opargs) m_highlightMap.insert(id, hl_attr); } +void Shell::handleHighlightGroupSet(const QVariantList& opargs) noexcept +{ + if (opargs.size() < 2 + || opargs.at(0).type() != QVariant::Type::ByteArray + || !opargs.at(1).canConvert()) { + qWarning() << "Unexpected arguments for hl_group_set:" << opargs; + return; + } + + const QString name{ m_nvim->decode(opargs.at(0).toByteArray()) }; + const uint64_t hl_id{ opargs.at(1).toULongLong() }; + + m_highlightGroupNameMap.insert(name, hl_id); +} void Shell::handleGridLine(const QVariantList& opargs) { @@ -1095,6 +1119,50 @@ void Shell::handleGridScroll(const QVariantList& opargs) update(neovimCursorRect()); } +void Shell::handleGuiAdaptiveColor(const QVariantList& opargs) noexcept +{ + if (opargs.size() < 2 + || !opargs.at(1).canConvert()) { + qWarning() << "Unexpected arguments for GuiAdaptiveColor:" << opargs; + return; + } + + const bool isEnabled{ opargs.at(1).toBool() }; + + emit setGuiAdaptiveColorEnabled(isEnabled); +} + +void Shell::handleGuiAdaptiveFont(const QVariantList& opargs) noexcept +{ + if (opargs.size() < 2 + || !opargs.at(1).canConvert()) { + qWarning() << "Unexpected arguments for GuiAdaptiveFont:" << opargs; + return; + } + + const bool isEnabled{ opargs.at(1).toBool() }; + + emit setGuiAdaptiveFontEnabled(isEnabled); +} + +void Shell::handleGuiAdaptiveStyle(const QVariantList& opargs) noexcept +{ + if (opargs.size() < 2 + || !opargs.at(1).canConvert()) { + qWarning() << "Unexpected arguments for GuiAdaptiveStyle:" << opargs; + return; + } + + const QString styleName { opargs.at(1).toByteArray() }; + + emit setGuiAdaptiveStyle(styleName); +} + +void Shell::handleGuiAdaptiveStyleList() noexcept +{ + emit showGuiAdaptiveStyleList(); +} + void Shell::paintEvent(QPaintEvent *ev) { if (!m_attached) { diff --git a/src/gui/shell.h b/src/gui/shell.h index a7f699a9e..d1d3f2f2c 100644 --- a/src/gui/shell.h +++ b/src/gui/shell.h @@ -53,6 +53,25 @@ class Shell: public ShellWidget bool neovimBusy() const; bool neovimAttached() const; + PopupMenu& getPopupMenu() noexcept + { + return m_pum; + } + + /// Lookup highlight by name from hl_group_set + HighlightAttribute GetHighlightGroup(const QString& name) const noexcept + { + const uint64_t hl_id{ m_highlightGroupNameMap.value(name) }; + return m_highlightMap.value(hl_id); + } + + /// Check if highlight exists in hl_group_set + bool IsHighlightGroup(const QString& name) const noexcept + { + const uint64_t hl_id{ m_highlightGroupNameMap.value(name) }; + return m_highlightMap.contains(hl_id); + } + /// Dispatches Neovim redraw notifications to T::handleRedraw template static void DispatchRedrawNotifications( @@ -77,6 +96,13 @@ class Shell: public ShellWidget void neovimShowtablineSet(int); void neovimShowContextMenu(); void fontChanged(); + void colorsChanged(); + + // GuiAdaptive Color/Font Signals + void setGuiAdaptiveColorEnabled(bool isEnabled); + void setGuiAdaptiveFontEnabled(bool isEnabled); + void setGuiAdaptiveStyle(const QString& styleName); + void showGuiAdaptiveStyleList(); public slots: void handleNeovimNotification(const QByteArray &name, const QVariantList& args); @@ -140,10 +166,17 @@ protected slots: virtual void handleGridResize(const QVariantList& opargs); virtual void handleDefaultColorsSet(const QVariantList& opargs); virtual void handleHighlightAttributeDefine(const QVariantList& opargs); + virtual void handleHighlightGroupSet(const QVariantList& opargs) noexcept; virtual void handleGridLine(const QVariantList& opargs); virtual void handleGridCursorGoto(const QVariantList& opargs); virtual void handleGridScroll(const QVariantList& opargs); + // GuiAdaptive Color/Font + virtual void handleGuiAdaptiveColor(const QVariantList& opargs) noexcept; + virtual void handleGuiAdaptiveFont(const QVariantList& opargs) noexcept; + virtual void handleGuiAdaptiveStyle(const QVariantList& opargs) noexcept; + virtual void handleGuiAdaptiveStyleList() noexcept; + void neovimMouseEvent(QMouseEvent *ev); virtual void mousePressEvent(QMouseEvent *ev) Q_DECL_OVERRIDE; virtual void mouseReleaseEvent(QMouseEvent *ev) Q_DECL_OVERRIDE; @@ -178,6 +211,9 @@ private slots: /// Modern 'ext_linegrid' highlight definition map QMap m_highlightMap; + /// Storage for hl_group_set, maps to hl_id in m_highlightMap + QMap m_highlightGroupNameMap; + /// Neovim mode descriptions from "mode_change", used by guicursor QVariantList m_modeInfo;