Support custom semi-native title buttons on Windows.
This commit is contained in:
parent
a110c6ea53
commit
2aa929dc91
4 changed files with 210 additions and 88 deletions
|
|
@ -104,18 +104,20 @@ void AbstractButton::setPointerCursor(bool enablePointerCursor) {
|
|||
}
|
||||
|
||||
void AbstractButton::setOver(bool over, StateChangeSource source) {
|
||||
if (over && !(_state & StateFlag::Over)) {
|
||||
auto was = _state;
|
||||
if (over == isOver()) {
|
||||
return;
|
||||
}
|
||||
const auto was = _state;
|
||||
if (over) {
|
||||
_state |= StateFlag::Over;
|
||||
Integration::Instance().registerLeaveSubscription(this);
|
||||
onStateChanged(was, source);
|
||||
} else if (!over && (_state & StateFlag::Over)) {
|
||||
auto was = _state;
|
||||
} else {
|
||||
_state &= ~State(StateFlag::Over);
|
||||
Integration::Instance().unregisterLeaveSubscription(this);
|
||||
onStateChanged(was, source);
|
||||
}
|
||||
onStateChanged(was, source);
|
||||
updateCursor();
|
||||
update();
|
||||
}
|
||||
|
||||
bool AbstractButton::setDown(
|
||||
|
|
@ -149,8 +151,11 @@ bool AbstractButton::setDown(
|
|||
}
|
||||
|
||||
void AbstractButton::updateCursor() {
|
||||
auto pointerCursor = _enablePointerCursor && (_state & StateFlag::Over);
|
||||
setCursor(pointerCursor ? style::cur_pointer : style::cur_default);
|
||||
const auto pointerCursor = _enablePointerCursor && isOver();
|
||||
if (_pointerCursor != pointerCursor) {
|
||||
_pointerCursor = pointerCursor;
|
||||
setCursor(_pointerCursor ? style::cur_pointer : style::cur_default);
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractButton::setDisabled(bool disabled) {
|
||||
|
|
|
|||
|
|
@ -16,22 +16,29 @@ class AbstractButton : public RpWidget {
|
|||
public:
|
||||
AbstractButton(QWidget *parent);
|
||||
|
||||
Qt::KeyboardModifiers clickModifiers() const {
|
||||
[[nodiscard]] Qt::KeyboardModifiers clickModifiers() const {
|
||||
return _modifiers;
|
||||
}
|
||||
|
||||
void setDisabled(bool disabled = true);
|
||||
virtual void clearState();
|
||||
bool isOver() const {
|
||||
[[nodiscard]] bool isOver() const {
|
||||
return _state & StateFlag::Over;
|
||||
}
|
||||
bool isDown() const {
|
||||
[[nodiscard]] bool isDown() const {
|
||||
return _state & StateFlag::Down;
|
||||
}
|
||||
bool isDisabled() const {
|
||||
[[nodiscard]] bool isDisabled() const {
|
||||
return _state & StateFlag::Disabled;
|
||||
}
|
||||
|
||||
void setSynteticOver(bool over) {
|
||||
setOver(over, StateChangeSource::ByPress);
|
||||
}
|
||||
void setSynteticDown(bool down, Qt::MouseButton button = Qt::LeftButton) {
|
||||
setDown(down, StateChangeSource::ByPress, {}, button);
|
||||
}
|
||||
|
||||
void setPointerCursor(bool enablePointerCursor);
|
||||
|
||||
void setAcceptBoth(bool acceptBoth = true);
|
||||
|
|
@ -95,9 +102,10 @@ private:
|
|||
|
||||
State _state = StateFlag::None;
|
||||
|
||||
bool _acceptBoth = false;
|
||||
Qt::KeyboardModifiers _modifiers;
|
||||
bool _enablePointerCursor = true;
|
||||
bool _pointerCursor = false;
|
||||
bool _acceptBoth = false;
|
||||
|
||||
Fn<void()> _clickedCallback;
|
||||
|
||||
|
|
|
|||
|
|
@ -65,30 +65,94 @@ void SetupSemiNativeSystemButtons(
|
|||
}, lifetime);
|
||||
}
|
||||
|
||||
class TitleControls::Button final : public IconButton {
|
||||
public:
|
||||
using IconButton::IconButton;
|
||||
object_ptr<AbstractButton> IconTitleButtons::create(
|
||||
not_null<QWidget*> parent,
|
||||
TitleControl control,
|
||||
const style::WindowTitle &st) {
|
||||
const auto make = [&](
|
||||
QPointer<IconButton> &my,
|
||||
const style::IconButton &st) {
|
||||
Expects(!my);
|
||||
|
||||
void setOver(bool over) {
|
||||
IconButton::setOver(over, StateChangeSource::ByPress);
|
||||
auto result = object_ptr<IconButton>(parent, st);
|
||||
my = result.data();
|
||||
return result;
|
||||
};
|
||||
switch (control) {
|
||||
case TitleControl::Minimize:
|
||||
return make(_minimize, st.minimize);
|
||||
case TitleControl::Maximize:
|
||||
return make(_maximizeRestore, st.maximize);
|
||||
case TitleControl::Close:
|
||||
return make(_close, st.close);
|
||||
}
|
||||
void setDown(bool down) {
|
||||
IconButton::setDown(
|
||||
down,
|
||||
StateChangeSource::ByPress,
|
||||
{},
|
||||
Qt::LeftButton);
|
||||
Unexpected("Control in IconTitleButtons::create.");
|
||||
}
|
||||
|
||||
void IconTitleButtons::updateState(
|
||||
bool active,
|
||||
bool maximized,
|
||||
const style::WindowTitle &st) {
|
||||
if (_minimize) {
|
||||
const auto minimize = active
|
||||
? &st.minimizeIconActive
|
||||
: &st.minimize.icon;
|
||||
const auto minimizeOver = active
|
||||
? &st.minimizeIconActiveOver
|
||||
: &st.minimize.iconOver;
|
||||
_minimize->setIconOverride(minimize, minimizeOver);
|
||||
}
|
||||
};
|
||||
if (_maximizeRestore) {
|
||||
if (maximized) {
|
||||
const auto restore = active
|
||||
? &st.restoreIconActive
|
||||
: &st.restoreIcon;
|
||||
const auto restoreOver = active
|
||||
? &st.restoreIconActiveOver
|
||||
: &st.restoreIconOver;
|
||||
_maximizeRestore->setIconOverride(restore, restoreOver);
|
||||
} else {
|
||||
const auto maximize = active
|
||||
? &st.maximizeIconActive
|
||||
: &st.maximize.icon;
|
||||
const auto maximizeOver = active
|
||||
? &st.maximizeIconActiveOver
|
||||
: &st.maximize.iconOver;
|
||||
_maximizeRestore->setIconOverride(maximize, maximizeOver);
|
||||
}
|
||||
}
|
||||
if (_close) {
|
||||
const auto close = active
|
||||
? &st.closeIconActive
|
||||
: &st.close.icon;
|
||||
const auto closeOver = active
|
||||
? &st.closeIconActiveOver
|
||||
: &st.close.iconOver;
|
||||
_close->setIconOverride(close, closeOver);
|
||||
}
|
||||
}
|
||||
|
||||
TitleControls::TitleControls(
|
||||
not_null<RpWidget*> parent,
|
||||
const style::WindowTitle &st,
|
||||
Fn<void(bool maximized)> maximize)
|
||||
: TitleControls(
|
||||
parent,
|
||||
st,
|
||||
std::make_unique<IconTitleButtons>(),
|
||||
std::move(maximize)) {
|
||||
}
|
||||
|
||||
TitleControls::TitleControls(
|
||||
not_null<RpWidget*> parent,
|
||||
const style::WindowTitle &st,
|
||||
std::unique_ptr<AbstractTitleButtons> buttons,
|
||||
Fn<void(bool maximized)> maximize)
|
||||
: _st(&st)
|
||||
, _minimize(parent, _st->minimize)
|
||||
, _maximizeRestore(parent, _st->maximize)
|
||||
, _close(parent, _st->close)
|
||||
, _buttons(std::move(buttons))
|
||||
, _minimize(_buttons->create(parent, Control::Minimize, st))
|
||||
, _maximizeRestore(_buttons->create(parent, Control::Maximize, st))
|
||||
, _close(_buttons->create(parent, Control::Close, st))
|
||||
, _maximizedState(parent->windowState()
|
||||
& (Qt::WindowMaximized | Qt::WindowFullScreen))
|
||||
, _activeState(parent->isActiveWindow()) {
|
||||
|
|
@ -216,7 +280,9 @@ void TitleControls::raise() {
|
|||
}
|
||||
|
||||
HitTestResult TitleControls::hitTest(QPoint point, int padding) const {
|
||||
const auto test = [&](const object_ptr<Button> &button, bool close) {
|
||||
const auto test = [&](
|
||||
const object_ptr<AbstractButton> &button,
|
||||
bool close) {
|
||||
return button && button->geometry().marginsAdded(
|
||||
{ close ? padding : 0, padding, close ? padding : 0, 0 }
|
||||
).contains(point);
|
||||
|
|
@ -233,23 +299,29 @@ HitTestResult TitleControls::hitTest(QPoint point, int padding) const {
|
|||
|
||||
void TitleControls::buttonOver(HitTestResult testResult) {
|
||||
const auto update = [&](
|
||||
const object_ptr<Button> &button,
|
||||
HitTestResult buttonTestResult) {
|
||||
const object_ptr<AbstractButton> &button,
|
||||
HitTestResult buttonTestResult,
|
||||
Control control) {
|
||||
const auto over = (testResult == buttonTestResult);
|
||||
if (const auto raw = button.data()) {
|
||||
raw->setOver(testResult == buttonTestResult);
|
||||
raw->setSynteticOver(over);
|
||||
}
|
||||
_buttons->notifySynteticOver(control, over);
|
||||
};
|
||||
update(_minimize, HitTestResult::Minimize);
|
||||
update(_maximizeRestore, HitTestResult::MaximizeRestore);
|
||||
update(_close, HitTestResult::Close);
|
||||
update(_minimize, HitTestResult::Minimize, Control::Minimize);
|
||||
update(
|
||||
_maximizeRestore,
|
||||
HitTestResult::MaximizeRestore,
|
||||
Control::Maximize);
|
||||
update(_close, HitTestResult::Close, Control::Close);
|
||||
}
|
||||
|
||||
void TitleControls::buttonDown(HitTestResult testResult) {
|
||||
const auto update = [&](
|
||||
const object_ptr<Button> &button,
|
||||
const object_ptr<AbstractButton> &button,
|
||||
HitTestResult buttonTestResult) {
|
||||
if (const auto raw = button.data()) {
|
||||
raw->setDown(testResult == buttonTestResult);
|
||||
raw->setSynteticDown(testResult == buttonTestResult);
|
||||
}
|
||||
};
|
||||
update(_minimize, HitTestResult::Minimize);
|
||||
|
|
@ -257,7 +329,7 @@ void TitleControls::buttonDown(HitTestResult testResult) {
|
|||
update(_close, HitTestResult::Close);
|
||||
}
|
||||
|
||||
TitleControls::Button *TitleControls::controlWidget(Control control) const {
|
||||
AbstractButton *TitleControls::controlWidget(Control control) const {
|
||||
switch (control) {
|
||||
case Control::Minimize: return _minimize;
|
||||
case Control::Maximize: return _maximizeRestore;
|
||||
|
|
@ -370,37 +442,7 @@ void TitleControls::handleWindowStateChanged(Qt::WindowState state) {
|
|||
}
|
||||
|
||||
void TitleControls::updateButtonsState() {
|
||||
const auto minimize = _activeState
|
||||
? &_st->minimizeIconActive
|
||||
: &_st->minimize.icon;
|
||||
const auto minimizeOver = _activeState
|
||||
? &_st->minimizeIconActiveOver
|
||||
: &_st->minimize.iconOver;
|
||||
_minimize->setIconOverride(minimize, minimizeOver);
|
||||
if (_maximizedState) {
|
||||
const auto restore = _activeState
|
||||
? &_st->restoreIconActive
|
||||
: &_st->restoreIcon;
|
||||
const auto restoreOver = _activeState
|
||||
? &_st->restoreIconActiveOver
|
||||
: &_st->restoreIconOver;
|
||||
_maximizeRestore->setIconOverride(restore, restoreOver);
|
||||
} else {
|
||||
const auto maximize = _activeState
|
||||
? &_st->maximizeIconActive
|
||||
: &_st->maximize.icon;
|
||||
const auto maximizeOver = _activeState
|
||||
? &_st->maximizeIconActiveOver
|
||||
: &_st->maximize.iconOver;
|
||||
_maximizeRestore->setIconOverride(maximize, maximizeOver);
|
||||
}
|
||||
const auto close = _activeState
|
||||
? &_st->closeIconActive
|
||||
: &_st->close.icon;
|
||||
const auto closeOver = _activeState
|
||||
? &_st->closeIconActiveOver
|
||||
: &_st->close.iconOver;
|
||||
_close->setIconOverride(close, closeOver);
|
||||
_buttons->updateState(_activeState, _maximizedState, *_st);
|
||||
}
|
||||
|
||||
DefaultTitleWidget::DefaultTitleWidget(not_null<RpWidget*> parent)
|
||||
|
|
@ -479,17 +521,34 @@ SeparateTitleControls::SeparateTitleControls(
|
|||
, controls(&wrap, st, std::move(maximize)) {
|
||||
}
|
||||
|
||||
SeparateTitleControls::SeparateTitleControls(
|
||||
QWidget *parent,
|
||||
const style::WindowTitle &st,
|
||||
std::unique_ptr<AbstractTitleButtons> buttons,
|
||||
Fn<void(bool maximized)> maximize)
|
||||
: wrap(parent)
|
||||
, controls(&wrap, st, std::move(buttons), std::move(maximize)) {
|
||||
}
|
||||
|
||||
std::unique_ptr<SeparateTitleControls> SetupSeparateTitleControls(
|
||||
not_null<RpWindow*> window,
|
||||
const style::WindowTitle &st,
|
||||
Fn<void(bool maximized)> maximize,
|
||||
rpl::producer<int> controlsTop) {
|
||||
auto result = std::make_unique<SeparateTitleControls>(
|
||||
window->body(),
|
||||
st,
|
||||
std::move(maximize));
|
||||
return SetupSeparateTitleControls(
|
||||
window,
|
||||
std::make_unique<SeparateTitleControls>(
|
||||
window->body(),
|
||||
st,
|
||||
std::move(maximize)),
|
||||
std::move(controlsTop));
|
||||
}
|
||||
|
||||
const auto raw = result.get();
|
||||
std::unique_ptr<SeparateTitleControls> SetupSeparateTitleControls(
|
||||
not_null<RpWindow*> window,
|
||||
std::unique_ptr<SeparateTitleControls> created,
|
||||
rpl::producer<int> controlsTop) {
|
||||
const auto raw = created.get();
|
||||
auto &lifetime = raw->wrap.lifetime();
|
||||
rpl::combine(
|
||||
window->body()->widthValue(),
|
||||
|
|
@ -516,7 +575,7 @@ std::unique_ptr<SeparateTitleControls> SetupSeparateTitleControls(
|
|||
|
||||
SetupSemiNativeSystemButtons(&raw->controls, window, lifetime);
|
||||
|
||||
return result;
|
||||
return created;
|
||||
}
|
||||
|
||||
} // namespace Platform
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ struct WindowTitle;
|
|||
namespace Ui {
|
||||
|
||||
class IconButton;
|
||||
class AbstractButton;
|
||||
class PlainShadow;
|
||||
class RpWindow;
|
||||
|
||||
|
|
@ -55,12 +56,57 @@ void SetupSemiNativeSystemButtons(
|
|||
rpl::lifetime &lifetime,
|
||||
Fn<bool()> filter = nullptr);
|
||||
|
||||
enum class TitleControl {
|
||||
Unknown,
|
||||
Minimize,
|
||||
Maximize,
|
||||
Close,
|
||||
};
|
||||
|
||||
class AbstractTitleButtons {
|
||||
public:
|
||||
[[nodiscard]] virtual object_ptr<AbstractButton> create(
|
||||
not_null<QWidget*> parent,
|
||||
TitleControl control,
|
||||
const style::WindowTitle &st) = 0;
|
||||
virtual void updateState(
|
||||
bool active,
|
||||
bool maximized,
|
||||
const style::WindowTitle &st) = 0;
|
||||
virtual void notifySynteticOver(TitleControl control, bool over) = 0;
|
||||
};
|
||||
|
||||
class IconTitleButtons final : public AbstractTitleButtons {
|
||||
public:
|
||||
object_ptr<AbstractButton> create(
|
||||
not_null<QWidget*> parent,
|
||||
TitleControl control,
|
||||
const style::WindowTitle &st) override;
|
||||
void updateState(
|
||||
bool active,
|
||||
bool maximized,
|
||||
const style::WindowTitle &st) override;
|
||||
void notifySynteticOver(TitleControl control, bool over) override {
|
||||
}
|
||||
|
||||
private:
|
||||
QPointer<IconButton> _minimize;
|
||||
QPointer<IconButton> _maximizeRestore;
|
||||
QPointer<IconButton> _close;
|
||||
|
||||
};
|
||||
|
||||
class TitleControls final {
|
||||
public:
|
||||
TitleControls(
|
||||
not_null<RpWidget*> parent,
|
||||
const style::WindowTitle &st,
|
||||
Fn<void(bool maximized)> maximize = nullptr);
|
||||
TitleControls(
|
||||
not_null<RpWidget*> parent,
|
||||
const style::WindowTitle &st,
|
||||
std::unique_ptr<AbstractTitleButtons> buttons,
|
||||
Fn<void(bool maximized)> maximize = nullptr);
|
||||
|
||||
void setStyle(const style::WindowTitle &st);
|
||||
[[nodiscard]] not_null<const style::WindowTitle*> st() const;
|
||||
|
|
@ -73,24 +119,16 @@ public:
|
|||
void buttonOver(HitTestResult testResult);
|
||||
void buttonDown(HitTestResult testResult);
|
||||
|
||||
enum class Control {
|
||||
Unknown,
|
||||
Minimize,
|
||||
Maximize,
|
||||
Close,
|
||||
};
|
||||
|
||||
using Control = TitleControl;
|
||||
struct Layout {
|
||||
std::vector<Control> left;
|
||||
std::vector<Control> right;
|
||||
};
|
||||
|
||||
private:
|
||||
class Button;
|
||||
|
||||
[[nodiscard]] not_null<RpWidget*> parent() const;
|
||||
[[nodiscard]] not_null<QWidget*> window() const;
|
||||
[[nodiscard]] Button *controlWidget(Control control) const;
|
||||
[[nodiscard]] AbstractButton *controlWidget(Control control) const;
|
||||
|
||||
void init(Fn<void(bool maximized)> maximize);
|
||||
void subscribeToStateChanges();
|
||||
|
|
@ -102,10 +140,11 @@ private:
|
|||
void handleWindowStateChanged(Qt::WindowState state = Qt::WindowNoState);
|
||||
|
||||
not_null<const style::WindowTitle*> _st;
|
||||
const std::unique_ptr<AbstractTitleButtons> _buttons;
|
||||
|
||||
object_ptr<Button> _minimize;
|
||||
object_ptr<Button> _maximizeRestore;
|
||||
object_ptr<Button> _close;
|
||||
object_ptr<AbstractButton> _minimize;
|
||||
object_ptr<AbstractButton> _maximizeRestore;
|
||||
object_ptr<AbstractButton> _close;
|
||||
|
||||
bool _maximizedState = false;
|
||||
bool _activeState = false;
|
||||
|
|
@ -143,6 +182,11 @@ struct SeparateTitleControls {
|
|||
QWidget *parent,
|
||||
const style::WindowTitle &st,
|
||||
Fn<void(bool maximized)> maximize);
|
||||
SeparateTitleControls(
|
||||
QWidget *parent,
|
||||
const style::WindowTitle &st,
|
||||
std::unique_ptr<AbstractTitleButtons> buttons,
|
||||
Fn<void(bool maximized)> maximize);
|
||||
|
||||
RpWidget wrap;
|
||||
TitleControls controls;
|
||||
|
|
@ -155,5 +199,11 @@ struct SeparateTitleControls {
|
|||
rpl::producer<int> controlsTop = nullptr)
|
||||
-> std::unique_ptr<SeparateTitleControls>;
|
||||
|
||||
[[nodiscard]] auto SetupSeparateTitleControls(
|
||||
not_null<RpWindow*> window,
|
||||
std::unique_ptr<SeparateTitleControls> created,
|
||||
rpl::producer<int> controlsTop = nullptr)
|
||||
-> std::unique_ptr<SeparateTitleControls>;
|
||||
|
||||
} // namespace Platform
|
||||
} // namespace Ui
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue