diff --git a/src/player/control-widget.cpp b/src/player/control-widget.cpp index efe5cd7b..76c9b2c3 100644 --- a/src/player/control-widget.cpp +++ b/src/player/control-widget.cpp @@ -18,10 +18,10 @@ ControlWidget::ControlWidget(FramelessWindow *parent) // title bar { - const auto title_bar = new TitleBar(parent); - title_bar->setObjectName("title-bar"); - title_bar->setHideOnFullScreen(false); - layout->addWidget(title_bar); + title_bar_ = parent->titlebar(); + title_bar_->setObjectName("title-bar"); + title_bar_->setHideOnFullScreen(false); + layout->addWidget(title_bar_); } layout->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Minimum, QSizePolicy::Expanding)); @@ -133,7 +133,8 @@ ControlWidget::ControlWidget(FramelessWindow *parent) bool ControlWidget::hideable() const { - return !control_bar_->geometry().contains(mapFromGlobal(QCursor::pos())) && + const auto pos = mapFromGlobal(QCursor::pos()); + return !title_bar_->geometry().contains(pos) && !control_bar_->geometry().contains(pos) && !speed_box_->view()->isVisible(); } diff --git a/src/player/control-widget.h b/src/player/control-widget.h index eb6458e1..896ba742 100644 --- a/src/player/control-widget.h +++ b/src/player/control-widget.h @@ -15,6 +15,8 @@ enum class PlaybackMode ANIMATED_IMAGE = 0x20, }; +class TitleBar; + class ControlWidget final : public QWidget { Q_OBJECT @@ -45,6 +47,7 @@ public slots: void validDruation(bool); private: + TitleBar *title_bar_{}; QWidget *control_bar_{}; Slider *time_slider_{}; ComboBox *speed_box_{}; diff --git a/src/resources/stylesheets/capturer.qss b/src/resources/stylesheets/capturer.qss index 2fb90011..d60481bd 100644 --- a/src/resources/stylesheets/capturer.qss +++ b/src/resources/stylesheets/capturer.qss @@ -366,21 +366,24 @@ NavigationBar QCheckBox::indicator { } /*///////////////////////////////////////////////////////////////*/ -TitleBar QCheckBox#icon-title { - font-size: 10pt; -} - -TitleBar QCheckBox#icon-title::indicator { +TitleBar QPushButton#icon { font-size: 8pt; - height: 1.5em; - width: 1.5em; - padding: 0.75em 1em; + width: 3em; - image: url(:/icons/capturer); + border: none; + padding: 0px; + border-radius: 0px; + + background-color: transparent; + + qproperty-iconSize: 26px; +} + +TitleBar QLabel#title { + font-size: 10pt; } -/* buttons */ TitleBar QCheckBox::indicator { font-size: 8pt; height: 1em; diff --git a/src/resources/stylesheets/player.qss b/src/resources/stylesheets/player.qss index fcfe2edc..7e189a78 100644 --- a/src/resources/stylesheets/player.qss +++ b/src/resources/stylesheets/player.qss @@ -8,24 +8,22 @@ ControlWidget TitleBar { background-color: rgba(25, 25, 25, 175); } -ControlWidget QCheckBox#icon-title { +ControlWidget TitleBar QPushButton#icon { + width: 0px; +} + +ControlWidget TitleBar QLabel#title { font-size: 10pt; color: white; padding-left: 0.75em; min-width: 8em; } -ControlWidget TitleBar QCheckBox#icon-title::indicator { - width: 0px; - height: 0px; - padding: 0px; -} - ControlWidget QCheckBox#pin-btn:hover, ControlWidget QCheckBox#min-btn:hover, ControlWidget QCheckBox#max-btn:hover, ControlWidget QCheckBox#full-btn:hover { - background: rgba(0, 0, 0, 75); + background: rgba(0, 0, 0, 100); } ControlWidget TitleBar QCheckBox#pin-btn::indicator:unchecked { @@ -75,7 +73,7 @@ QWidget#control-bar QCheckBox::indicator { } QWidget#control-bar QCheckBox::indicator:hover { - background-color: rgba(0, 0, 0, 75); + background-color: rgba(0, 0, 0, 100); } /* pause button */ @@ -129,7 +127,7 @@ QWidget#control-bar ComboBox#speed-box::down-arrow { QWidget#control-bar ComboBox#speed-box:hover { color: white; - background-color: rgba(0, 0, 0, 50); + background-color: rgba(0, 0, 0, 100); } diff --git a/src/widgets/framelesswindow.cpp b/src/widgets/framelesswindow.cpp index dab12123..5164de55 100644 --- a/src/widgets/framelesswindow.cpp +++ b/src/widgets/framelesswindow.cpp @@ -2,6 +2,7 @@ #include "logging.h" #include "platforms/window-effect.h" +#include "titlebar.h" #include #include @@ -57,15 +58,12 @@ bool FramelessWindow::isSizeFixed() const return !minsize.isEmpty() && !maxsize.isEmpty() && (minsize == maxsize); } -void FramelessWindow::maximize(const bool state) { state ? showMaximized() : showNormal(); } - -void FramelessWindow::toggleMaximized() { !isMaximized() ? showMaximized() : showNormal(); } - -void FramelessWindow::minimize(const bool state) { state ? showMinimized() : showNormal(); } - -void FramelessWindow::fullscreen(const bool state) { state ? showFullScreen() : showNormal(); } +TitleBar *FramelessWindow::titlebar() +{ + if (!titlebar_) titlebar_ = new TitleBar(this); -void FramelessWindow::toggleFullScreen() { isFullScreen() ? showNormal() : showFullScreen(); } + return titlebar_; +} void FramelessWindow::toggleTransparentInput() { @@ -113,17 +111,6 @@ void FramelessWindow::hideEvent(QHideEvent *event) QWidget::hideEvent(event); } -void FramelessWindow::changeEvent(QEvent *event) -{ - if (event->type() == QEvent::WindowStateChange) { - if (windowState() == Qt::WindowNoState) emit normalized(); - if (windowState() & Qt::WindowMinimized) emit minimized(); - if (windowState() & Qt::WindowMaximized) emit maximized(); - if (windowState() & Qt::WindowFullScreen) emit fullscreened(); - } - QWidget::changeEvent(event); -} - #ifdef Q_OS_LINUX void FramelessWindow::updateCursor(const Qt::Edges edges) { @@ -301,6 +288,14 @@ bool FramelessWindow::nativeEvent(const QByteArray& eventType, void *message, Q_ } } + if (res == HTCLIENT && titlebar_ && titlebar_->isVisible()) { + if (const auto pos = mapFromGlobal({ GET_X_LPARAM(wmsg->lParam), GET_Y_LPARAM(wmsg->lParam) }); + titlebar_->geometry().contains(pos) && !titlebar_->isInSystemButtons(pos)) { + *result = HTCAPTION; + return true; + } + } + *result = static_cast(res); return true; } diff --git a/src/widgets/framelesswindow.h b/src/widgets/framelesswindow.h index 600ba225..a694b40f 100644 --- a/src/widgets/framelesswindow.h +++ b/src/widgets/framelesswindow.h @@ -1,6 +1,7 @@ #ifndef CAPTURER_FRAMELESS_WINDOW_H #define CAPTURER_FRAMELESS_WINDOW_H +#include #include #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -9,6 +10,8 @@ #define Q_NATIVE_EVENT_RESULT long #endif +class TitleBar; + class FramelessWindow : public QWidget { Q_OBJECT @@ -17,23 +20,15 @@ class FramelessWindow : public QWidget [[nodiscard]] bool isSizeFixed() const; + TitleBar * titlebar(); + public slots: - void maximize(bool = true); - void toggleMaximized(); - void minimize(bool = true); - void fullscreen(bool = true); - void toggleFullScreen(); void toggleTransparentInput(); signals: void hidden(); void closed(); - void normalized(); - void maximized(); - void minimized(); - void fullscreened(); - protected: void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; @@ -41,7 +36,8 @@ public slots: void closeEvent(QCloseEvent *) override; void hideEvent(QHideEvent *event) override; - void changeEvent(QEvent *) override; + + QPointer titlebar_{}; bool transparent_input_{}; diff --git a/src/widgets/platforms/window-effect-windows.cpp b/src/widgets/platforms/window-effect-windows.cpp index 46f44d5a..08e0624c 100644 --- a/src/widgets/platforms/window-effect-windows.cpp +++ b/src/widgets/platforms/window-effect-windows.cpp @@ -2,6 +2,7 @@ #if _WIN32 +#include #include #include #include diff --git a/src/widgets/platforms/window-effect.h b/src/widgets/platforms/window-effect.h index a52df530..2f3c814d 100644 --- a/src/widgets/platforms/window-effect.h +++ b/src/widgets/platforms/window-effect.h @@ -4,7 +4,6 @@ #if _WIN32 #include -#include enum WINDOWCOMPOSITIONATTRIB : DWORD { diff --git a/src/widgets/titlebar.cpp b/src/widgets/titlebar.cpp index 20e1e00d..df24c059 100644 --- a/src/widgets/titlebar.cpp +++ b/src/widgets/titlebar.cpp @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -13,13 +15,13 @@ #include #endif -static void pinWindow(QWidget *win, bool top = true) +static void SetWindowStayOnTop(QWidget *win, bool top = true) { if (!win || !win->winId()) return; #ifdef Q_OS_WIN - auto hwnd = reinterpret_cast(win->winId()); - ::SetWindowPos(hwnd, top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + ::SetWindowPos(reinterpret_cast(win->winId()), top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE); #else win->setWindowFlag(Qt::WindowStaysOnTopHint, top); win->show(); @@ -30,106 +32,115 @@ TitleBar::TitleBar(FramelessWindow *parent) : QWidget(parent), window_(parent) { setAttribute(Qt::WA_StyledBackground); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - const auto layout = new QHBoxLayout(); + const auto layout = new QHBoxLayout(this); layout->setSpacing(0); layout->setContentsMargins({}); - setLayout(layout); - // icon & title - const auto icon = new QCheckBox(window_->windowTitle()); - icon->setAttribute(Qt::WA_TransparentForMouseEvents); - icon->setObjectName("icon-title"); - layout->addWidget(icon); + // icon + icon_btn_ = new QPushButton(); + icon_btn_->setAttribute(Qt::WA_TransparentForMouseEvents); + icon_btn_->setObjectName("icon"); + icon_btn_->setIcon(window_->windowIcon()); + layout->addWidget(icon_btn_, 0, Qt::AlignCenter); + + // title + title_label_ = new QLabel(window_->windowTitle()); + title_label_->setObjectName("title"); + layout->addWidget(title_label_); // blank layout->addStretch(); // pin button if (window_->windowFlags() & Qt::WindowStaysOnTopHint) { - const auto pin_btn = new QCheckBox(this); - pin_btn->setObjectName("pin-btn"); - pin_btn->setCheckable(true); - pin_btn->setChecked(window_->windowFlags() & Qt::WindowStaysOnTopHint); - connect(pin_btn, &QCheckBox::toggled, [=, this](auto checked) { pinWindow(window_, checked); }); - layout->addWidget(pin_btn, 0, Qt::AlignTop); + pin_btn_ = new QCheckBox(this); + pin_btn_->setObjectName("pin-btn"); + pin_btn_->setCheckable(true); + pin_btn_->setChecked(window_->windowFlags() & Qt::WindowStaysOnTopHint); + connect(pin_btn_, &QCheckBox::toggled, + [this](auto checked) { SetWindowStayOnTop(window_, checked); }); + layout->addWidget(pin_btn_, 0, Qt::AlignTop); } // minimize button if (window_->windowFlags() & Qt::WindowMinimizeButtonHint) { - const auto min_btn = new QCheckBox(this); - min_btn->setObjectName("min-btn"); - min_btn->setCheckable(false); - connect(min_btn, &QCheckBox::clicked, window_, &QWidget::showMinimized); - layout->addWidget(min_btn, 0, Qt::AlignTop); + min_btn_ = new QCheckBox(this); + min_btn_->setObjectName("min-btn"); + min_btn_->setCheckable(false); + connect(min_btn_, &QCheckBox::clicked, window_, &QWidget::showMinimized); + layout->addWidget(min_btn_, 0, Qt::AlignTop); } // maximize button if (window_->windowFlags() & Qt::WindowMaximizeButtonHint) { - const auto max_btn = new QCheckBox(this); - max_btn->setObjectName("max-btn"); - max_btn->setCheckable(true); - connect(max_btn, &QCheckBox::toggled, window_, &FramelessWindow::maximize); - connect(window_, &FramelessWindow::maximized, [=]() { max_btn->setChecked(true); }); - connect(window_, &FramelessWindow::normalized, [=]() { max_btn->setChecked(false); }); - layout->addWidget(max_btn, 0, Qt::AlignTop); + max_btn_ = new QCheckBox(this); + max_btn_->setObjectName("max-btn"); + max_btn_->setCheckable(true); + connect(max_btn_, &QCheckBox::clicked, + [this](int) { window_->isMaximized() ? window_->showNormal() : window_->showMaximized(); }); + layout->addWidget(max_btn_, 0, Qt::AlignTop); } // fullscreen button if (window_->windowFlags() & Qt::WindowFullscreenButtonHint) { - const auto full_btn = new QCheckBox(this); - full_btn->setObjectName("full-btn"); - full_btn->setCheckable(true); - connect(full_btn, &QCheckBox::toggled, window_, &FramelessWindow::fullscreen); - connect(window_, &FramelessWindow::fullscreened, [=]() { full_btn->setChecked(true); }); - connect(window_, &FramelessWindow::normalized, [=]() { full_btn->setChecked(false); }); - layout->addWidget(full_btn, 0, Qt::AlignTop); + full_btn_ = new QCheckBox(this); + full_btn_->setObjectName("full-btn"); + full_btn_->setCheckable(true); + connect(full_btn_, &QCheckBox::clicked, + [this] { window_->isFullScreen() ? window_->showNormal() : window_->showFullScreen(); }); + layout->addWidget(full_btn_, 0, Qt::AlignTop); } - connect(window_, &FramelessWindow::fullscreened, [this]() { setVisible(!hide_on_fullscreen_); }); - connect(window_, &FramelessWindow::normalized, this, &QWidget::show); - connect(new QShortcut(Qt::Key_F11, window_), &QShortcut::activated, window_, - &FramelessWindow::toggleFullScreen); // close button if (window_->windowFlags() & Qt::WindowCloseButtonHint) { - const auto close_btn = new QCheckBox(this); - close_btn->setObjectName("close-btn"); - close_btn->setCheckable(false); - connect(close_btn, &QCheckBox::clicked, window_, &QWidget::close); - layout->addWidget(close_btn, 0, Qt::AlignTop); + close_btn_ = new QCheckBox(this); + close_btn_->setObjectName("close-btn"); + close_btn_->setCheckable(false); + connect(close_btn_, &QCheckBox::clicked, window_, &QWidget::close); + layout->addWidget(close_btn_, 0, Qt::AlignTop); } - // title - connect(window_, &QWidget::windowTitleChanged, icon, &QCheckBox::setText); + // + connect(new QShortcut(Qt::Key_F11, window_), &QShortcut::activated, full_btn_, &QCheckBox::click); + + window_->installEventFilter(this); } -void TitleBar::mousePressEvent(QMouseEvent *event) +bool TitleBar::isInSystemButtons(const QPoint& pos) const { - if (!window_->isFullScreen() && event->button() == Qt::LeftButton) { - dragmove_status_ = 1; - return; - } - - QWidget::mousePressEvent(event); + return (pin_btn_ && pin_btn_->geometry().contains(pos)) || + (min_btn_ && min_btn_->geometry().contains(pos)) || + (max_btn_ && max_btn_->geometry().contains(pos)) || + (full_btn_ && full_btn_->geometry().contains(pos)) || + (close_btn_ && close_btn_->geometry().contains(pos)); } -void TitleBar::mouseMoveEvent(QMouseEvent *event) +void TitleBar::mouseDoubleClickEvent(QMouseEvent *) { - if (dragmove_status_ == 1) { - dragmove_status_ = 2; - window_->windowHandle()->startSystemMove(); - return; - } - - QWidget::mouseMoveEvent(event); + if (max_btn_) max_btn_->click(); } -void TitleBar::mouseReleaseEvent(QMouseEvent *event) +bool TitleBar::eventFilter(QObject *obj, QEvent *event) { - dragmove_status_ = 0; - QWidget::mouseReleaseEvent(event); -} + if (obj == window_) { + switch (event->type()) { + case QEvent::WindowIconChange: + if (icon_btn_) icon_btn_->setIcon(window_->windowIcon()); + break; + case QEvent::WindowTitleChange: + if (title_label_) title_label_->setText(window_->windowTitle()); + break; + case QEvent::WindowStateChange: + if (max_btn_) max_btn_->setChecked(window_->isMaximized()); + if (full_btn_) full_btn_->setChecked(window_->isFullScreen()); + + if (window_->windowState() == Qt::WindowNoState && !isVisible()) show(); + break; + default: break; + } + } -void TitleBar::mouseDoubleClickEvent(QMouseEvent *) { window_->toggleMaximized(); } + return QWidget::eventFilter(obj, event); +} \ No newline at end of file diff --git a/src/widgets/titlebar.h b/src/widgets/titlebar.h index ec825b0b..f891b78a 100644 --- a/src/widgets/titlebar.h +++ b/src/widgets/titlebar.h @@ -3,6 +3,11 @@ #include "framelesswindow.h" +#include + +class QAbstractButton; +class QLabel; + class TitleBar : public QWidget { Q_OBJECT @@ -11,16 +16,23 @@ class TitleBar : public QWidget void setHideOnFullScreen(bool value = true) { hide_on_fullscreen_ = value; } -protected: - void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; + [[nodiscard]] bool isInSystemButtons(const QPoint& pos) const; +protected: void mouseDoubleClickEvent(QMouseEvent *event) override; + bool eventFilter(QObject *obj, QEvent *event) override; + private: FramelessWindow *window_{}; bool hide_on_fullscreen_{ true }; - int dragmove_status_{}; + + QPointer icon_btn_{}; + QPointer title_label_{}; + QPointer pin_btn_{}; + QPointer min_btn_{}; + QPointer max_btn_{}; + QPointer full_btn_{}; + QPointer close_btn_{}; }; #endif //! CAPTURER_TITLE_BAR_H