diff --git a/Telegram/Resources/icons/mediaview/title_button_close_mac.png b/Telegram/Resources/icons/mediaview/title_button_close_mac.png new file mode 100644 index 000000000..8df83a2d7 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_close_mac.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_close_mac@2x.png b/Telegram/Resources/icons/mediaview/title_button_close_mac@2x.png new file mode 100644 index 000000000..e35cd25b9 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_close_mac@2x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_close_mac@3x.png b/Telegram/Resources/icons/mediaview/title_button_close_mac@3x.png new file mode 100644 index 000000000..ede3dd807 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_close_mac@3x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_mac.png b/Telegram/Resources/icons/mediaview/title_button_mac.png new file mode 100644 index 000000000..258c40348 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_mac.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_mac@2x.png b/Telegram/Resources/icons/mediaview/title_button_mac@2x.png new file mode 100644 index 000000000..411f40563 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_mac@2x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_mac@3x.png b/Telegram/Resources/icons/mediaview/title_button_mac@3x.png new file mode 100644 index 000000000..e9add22c2 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_mac@3x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_maximize_mac.png b/Telegram/Resources/icons/mediaview/title_button_maximize_mac.png new file mode 100644 index 000000000..76b05acab Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_maximize_mac.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_maximize_mac@2x.png b/Telegram/Resources/icons/mediaview/title_button_maximize_mac@2x.png new file mode 100644 index 000000000..4a09bdeb6 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_maximize_mac@2x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_maximize_mac@3x.png b/Telegram/Resources/icons/mediaview/title_button_maximize_mac@3x.png new file mode 100644 index 000000000..b832a810e Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_maximize_mac@3x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_minimize_mac.png b/Telegram/Resources/icons/mediaview/title_button_minimize_mac.png new file mode 100644 index 000000000..66e79a164 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_minimize_mac.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_minimize_mac@2x.png b/Telegram/Resources/icons/mediaview/title_button_minimize_mac@2x.png new file mode 100644 index 000000000..873421a38 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_minimize_mac@2x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_minimize_mac@3x.png b/Telegram/Resources/icons/mediaview/title_button_minimize_mac@3x.png new file mode 100644 index 000000000..56ad3aceb Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_minimize_mac@3x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_shadow_mac.png b/Telegram/Resources/icons/mediaview/title_button_shadow_mac.png new file mode 100644 index 000000000..10b33a61a Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_shadow_mac.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_shadow_mac@2x.png b/Telegram/Resources/icons/mediaview/title_button_shadow_mac@2x.png new file mode 100644 index 000000000..5b62a0987 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_shadow_mac@2x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_shadow_mac@3x.png b/Telegram/Resources/icons/mediaview/title_button_shadow_mac@3x.png new file mode 100644 index 000000000..a358b377e Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_shadow_mac@3x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_shrink_mac.png b/Telegram/Resources/icons/mediaview/title_button_shrink_mac.png new file mode 100644 index 000000000..b5d8829bc Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_shrink_mac.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_shrink_mac@2x.png b/Telegram/Resources/icons/mediaview/title_button_shrink_mac@2x.png new file mode 100644 index 000000000..249011ff3 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_shrink_mac@2x.png differ diff --git a/Telegram/Resources/icons/mediaview/title_button_shrink_mac@3x.png b/Telegram/Resources/icons/mediaview/title_button_shrink_mac@3x.png new file mode 100644 index 000000000..fda973db0 Binary files /dev/null and b/Telegram/Resources/icons/mediaview/title_button_shrink_mac@3x.png differ diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index 5130bc448..651d0c8da 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -321,6 +321,19 @@ mediaviewTitle: WindowTitle(defaultWindowTitle) { closeIconActive: mediaviewTitleClose; closeIconActiveOver: mediaviewTitleClose; } + +mediaviewTitleButtonMac: icon{ + { "mediaview/title_button_shadow_mac", windowShadowFg }, + { "mediaview/title_button_mac", mediaviewControlFg }, +}; +mediaviewTitleMinimizeMac: icon {{ "mediaview/title_button_minimize_mac", mediaviewBg }}; +mediaviewTitleMaximizeMac: icon {{ "mediaview/title_button_maximize_mac", mediaviewBg }}; +mediaviewTitleRestoreMac: icon {{ "mediaview/title_button_shrink_mac", mediaviewBg }}; +mediaviewTitleCloseMac: icon {{ "mediaview/title_button_close_mac", mediaviewBg }}; +mediaviewTitleCloseMacPadding: margins(8px, 4px, 0px, 4px); +mediaviewTitleMinimizeMacPadding: margins(0px, 4px, 0px, 4px); +mediaviewTitleMaximizeMacPadding: margins(0px, 4px, 8px, 4px); + mediaviewShadowTop: icon{{ "mediaview/shadow_top", windowShadowFg }}; mediaviewShadowBottom: icon{{ "mediaview/shadow_bottom", windowShadowFg }}; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index b5d3498d4..b36d7a005 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -3080,6 +3080,7 @@ void OverlayWidget::displayFinished() { //Ui::Platform::UpdateOverlayed(_window); showAndActivate(); } else if (isMinimized()) { + _helper->beforeShow(_fullscreen); showAndActivate(); } else { activate(); diff --git a/Telegram/SourceFiles/platform/mac/overlay_widget_mac.h b/Telegram/SourceFiles/platform/mac/overlay_widget_mac.h index ef9c6690a..2fa33447b 100644 --- a/Telegram/SourceFiles/platform/mac/overlay_widget_mac.h +++ b/Telegram/SourceFiles/platform/mac/overlay_widget_mac.h @@ -9,6 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_overlay_widget.h" +template +class object_ptr; + +namespace Ui { +class AbstractButton; +} // namespace Ui + +namespace Ui::Platform { +enum class TitleControl; +} // namespace Ui::Platform + namespace Platform { class MacOverlayWidgetHelper final : public OverlayWidgetHelper { @@ -22,15 +33,22 @@ public: void afterShow(bool fullscreen) override; void notifyFileDialogShown(bool shown) override; void minimize(not_null window) override; + void clearState() override; + void setControlsOpacity(float64 opacity) override; private: + using Control = Ui::Platform::TitleControl; struct Data; - void activate(int button); // NSWindowButton + void activate(Control control); void resolveNative(); void updateStyles(bool fullscreen); void refreshButtons(bool fullscreen); + object_ptr create( + not_null parent, + Control control); + std::unique_ptr _data; }; diff --git a/Telegram/SourceFiles/platform/mac/overlay_widget_mac.mm b/Telegram/SourceFiles/platform/mac/overlay_widget_mac.mm index 877b8ea8a..3429763d6 100644 --- a/Telegram/SourceFiles/platform/mac/overlay_widget_mac.mm +++ b/Telegram/SourceFiles/platform/mac/overlay_widget_mac.mm @@ -7,85 +7,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/mac/overlay_widget_mac.h" +#include "base/object_ptr.h" +#include "ui/platform/ui_platform_window_title.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/rp_window.h" +#include "styles/style_media_view.h" #include #include -@interface ButtonHandler : NSObject { -} - -- (instancetype) initWithCallback:(Fn)callback; -- (void) close:(id) sender; -- (void) miniaturize:(id) sender; -- (void) zoom:(id) sender; - -@end // @interface ButtonHandler - -@implementation ButtonHandler { - Fn _callback; -} - -- (instancetype) initWithCallback:(Fn)callback { - _callback = std::move(callback); - return [super init]; -} - -- (void) close:(id) sender { - _callback(NSWindowCloseButton); -} - -- (void) miniaturize:(id) sender { - _callback(NSWindowMiniaturizeButton); -} - -- (void) zoom:(id) sender { - _callback(NSWindowZoomButton); -} - -@end // @implementation ButtonHandler - namespace Platform { namespace { -[[nodiscard]] base::flat_map ButtonGeometries() { - auto result = base::flat_map(); - auto normal = QWidget(); - normal.hide(); - normal.createWinId(); - const auto view = reinterpret_cast(normal.winId()); - const auto window = [view window]; - const auto process = [&](NSWindowButton type) { - if (const auto button = [window standardWindowButton:type]) { - result.emplace(int(type), [button frame]); - } - }; - process(NSWindowCloseButton); - process(NSWindowMiniaturizeButton); - process(NSWindowZoomButton); - - const auto full = [window frame]; - const auto inner = [window contentRectForFrameRect:full].size.height; - const auto height = std::max(full.size.height - inner, 0.); - - result[int(NSWindowToolbarButton)] = { CGPoint(), CGSize{ full.size.width, height }}; - return result; -} +using namespace Media::View; } // namespace struct MacOverlayWidgetHelper::Data { const not_null window; const Fn maximize; - const base::flat_map buttons; - ButtonHandler *handler = nil; + object_ptr buttonClose = { nullptr }; + object_ptr buttonMinimize = { nullptr }; + object_ptr buttonMaximize = { nullptr }; + rpl::event_stream<> activations; + rpl::variable masterOpacity = 1.; + rpl::variable maximized = false; + rpl::event_stream<> clearStateRequests; + bool anyOver = false; NSWindow * __weak native = nil; - NSButton * __weak closeNative = nil; - NSButton * __weak miniaturizeNative = nil; - NSButton * __weak zoomNative = nil; - NSButton * __weak close = nil; - NSButton * __weak miniaturize = nil; - NSButton * __weak zoom = nil; }; MacOverlayWidgetHelper::MacOverlayWidgetHelper( @@ -94,24 +43,20 @@ MacOverlayWidgetHelper::MacOverlayWidgetHelper( : _data(std::make_unique(Data{ .window = window, .maximize = std::move(maximize), - .buttons = ButtonGeometries(), - .handler = [[ButtonHandler alloc] initWithCallback:[=](NSWindowButton button) { - activate(int(button)); - }] })) { + _data->buttonClose = create(window, Control::Close); + _data->buttonMinimize = create(window, Control::Minimize); + _data->buttonMaximize = create(window, Control::Maximize); } -MacOverlayWidgetHelper::~MacOverlayWidgetHelper() { - [_data->handler release]; - _data->handler = nil; -} +MacOverlayWidgetHelper::~MacOverlayWidgetHelper() = default; -void MacOverlayWidgetHelper::activate(int button) { +void MacOverlayWidgetHelper::activate(Control control) { const auto fullscreen = (_data->window->windowHandle()->flags() & Qt::FramelessWindowHint); - switch (NSWindowButton(button)) { - case NSWindowCloseButton: _data->window->close(); return; - case NSWindowMiniaturizeButton: [_data->native miniaturize:_data->handler]; return; - case NSWindowZoomButton: _data->maximize(!fullscreen); return; + switch (control) { + case Control::Close: _data->window->close(); return; + case Control::Minimize: [_data->native miniaturize:_data->native]; return; + case Control::Maximize: _data->maximize(!fullscreen); return; } } @@ -119,6 +64,7 @@ void MacOverlayWidgetHelper::beforeShow(bool fullscreen) { _data->window->setAttribute(Qt::WA_MacAlwaysShowToolWindow, !fullscreen); _data->window->windowHandle()->setFlag(Qt::FramelessWindowHint, fullscreen); updateStyles(fullscreen); + clearState(); } void MacOverlayWidgetHelper::afterShow(bool fullscreen) { @@ -133,6 +79,8 @@ void MacOverlayWidgetHelper::resolveNative() { } void MacOverlayWidgetHelper::updateStyles(bool fullscreen) { + _data->maximized = fullscreen; + resolveNative(); if (!_data->native) { return; @@ -153,85 +101,23 @@ void MacOverlayWidgetHelper::refreshButtons(bool fullscreen) { Expects(_data->native != nullptr); const auto window = _data->native; - auto next = CGPoint(); - const auto added = [&](NSRect frame) { - const auto left = frame.origin.x + frame.size.width * 1.5; - const auto top = frame.origin.y; - if (next.x < left) { - next.x = left; + const auto process = [&](NSWindowButton type) { + if (const auto button = [window standardWindowButton:type]) { + [button setHidden:YES]; } - next.y = top; }; - for (const auto &[type, frame] : _data->buttons) { - added(frame); - } - const auto skip = fullscreen - ? _data->buttons.find(int(NSWindowToolbarButton))->second.size.height - : 0.; - const auto process = [&](auto native, auto custom, NSWindowButton type, auto action) { - auto retained = (NSButton*)nil; - while (const auto button = [window standardWindowButton:type]) { - if ([button superview] != [window contentView]) { - *native = button; - [button setHidden:YES]; - break; - } else if (button == *custom) { - retained = [button retain]; - } - [button removeFromSuperview]; - } - const auto i = _data->buttons.find(int(type)); - const auto frame = [&](NSButton *button) { - if (i != end(_data->buttons)) { - auto origin = i->second.origin; - origin.y += skip; - return NSRect{ origin, i->second.size }; - } - const auto size = [button frame].size; - auto result = NSRect{ next, size }; - added(result); - result.origin.y += skip; - return result; - }; - if (!retained) { - if (*custom) { - retained = *custom; - [retained retain]; - [retained removeFromSuperview]; - } else { - const auto style = NSWindowStyleMaskTitled - | NSWindowStyleMaskClosable - | NSWindowStyleMaskMiniaturizable - | NSWindowStyleMaskResizable; - *custom - = retained - = [NSWindow standardWindowButton:type forStyleMask:style]; - [retained setTarget:_data->handler]; - [retained setAction:action]; - [retained retain]; - } - } - [[window contentView] addSubview:retained]; - [retained setFrame:frame(retained)]; - [retained setEnabled:YES]; - [retained setHidden:NO]; - [retained release]; - }; - process( - &_data->closeNative, - &_data->close, - NSWindowCloseButton, - @selector(close:)); - process( - &_data->miniaturizeNative, - &_data->miniaturize, - NSWindowMiniaturizeButton, - @selector(miniaturize:)); - process( - &_data->zoomNative, - &_data->zoom, - NSWindowZoomButton, - @selector(zoom:)); + process(NSWindowCloseButton); + process(NSWindowMiniaturizeButton); + process(NSWindowZoomButton); + _data->buttonClose->moveToLeft(0, 0); + _data->buttonClose->raise(); + _data->buttonClose->show(); + _data->buttonMinimize->moveToLeft(_data->buttonClose->width(), 0); + _data->buttonMinimize->raise(); + _data->buttonMinimize->show(); + _data->buttonMaximize->moveToLeft(_data->buttonClose->width() + _data->buttonMinimize->width(), 0); + _data->buttonMaximize->raise(); + _data->buttonMaximize->show(); } void MacOverlayWidgetHelper::notifyFileDialogShown(bool shown) { @@ -250,10 +136,142 @@ void MacOverlayWidgetHelper::notifyFileDialogShown(bool shown) { void MacOverlayWidgetHelper::minimize(not_null window) { resolveNative(); if (_data->native) { - [_data->native miniaturize:_data->handler]; + [_data->native miniaturize:_data->native]; } } +void MacOverlayWidgetHelper::clearState() { + _data->clearStateRequests.fire({}); +} + +void MacOverlayWidgetHelper::setControlsOpacity(float64 opacity) { + _data->masterOpacity = opacity; +} + +object_ptr MacOverlayWidgetHelper::create( + not_null parent, + Control control) { + auto result = object_ptr(parent); + const auto raw = result.data(); + + raw->setClickedCallback([=] { activate(control); }); + + struct State { + Ui::Animations::Simple animation; + float64 progress = -1.; + QImage frame; + bool maximized = false; + bool anyOver = false; + bool over = false; + }; + const auto state = raw->lifetime().make_state(); + + rpl::merge( + _data->masterOpacity.changes() | rpl::to_empty, + _data->maximized.changes() | rpl::to_empty + ) | rpl::start_with_next([=] { + raw->update(); + }, raw->lifetime()); + + _data->clearStateRequests.events( + ) | rpl::start_with_next([=] { + raw->clearState(); + raw->update(); + state->over = raw->isOver(); + _data->anyOver = false; + state->animation.stop(); + }, raw->lifetime()); + + struct Info { + const style::icon *icon = nullptr; + style::margins padding; + }; + const auto info = [&]() -> Info { + switch (control) { + case Control::Minimize: + return { &st::mediaviewTitleMinimizeMac, st::mediaviewTitleMinimizeMacPadding }; + case Control::Maximize: + return { &st::mediaviewTitleMaximizeMac, st::mediaviewTitleMaximizeMacPadding }; + case Control::Close: + return { &st::mediaviewTitleCloseMac, st::mediaviewTitleCloseMacPadding }; + } + Unexpected("Value in DefaultOverlayWidgetHelper::Buttons::create."); + }(); + const auto icon = info.icon; + + raw->resize(QRect(QPoint(), icon->size()).marginsAdded(info.padding).size()); + state->frame = QImage( + icon->size() * style::DevicePixelRatio(), + QImage::Format_ARGB32_Premultiplied); + state->frame.setDevicePixelRatio(style::DevicePixelRatio()); + + const auto updateOver = [=] { + const auto over = raw->isOver(); + if (state->over == over) { + return; + } + state->over = over; + const auto anyOver = over + || _data->buttonClose->isOver() + || _data->buttonMinimize->isOver() + || _data->buttonMaximize->isOver(); + if (_data->anyOver != anyOver) { + _data->anyOver = anyOver; + _data->buttonClose->update(); + _data->buttonMinimize->update(); + _data->buttonMaximize->update(); + } + state->animation.start( + [=] { raw->update(); }, + state->over ? 0. : 1., + state->over ? 1. : 0., + st::mediaviewFadeDuration); + }; + + const auto prepareFrame = [=] { + const auto progress = state->animation.value(state->over ? 1. : 0.); + const auto maximized = _data->maximized.current(); + const auto anyOver = _data->anyOver; + if (state->progress == progress + && state->maximized == maximized + && state->anyOver == anyOver) { + return; + } + state->progress = progress; + state->maximized = maximized; + state->anyOver = anyOver; + auto current = icon; + if (control == Control::Maximize) { + current = maximized ? &st::mediaviewTitleRestoreMac : icon; + } + state->frame.fill(Qt::transparent); + + auto q = QPainter(&state->frame); + const auto normal = maximized + ? kMaximizedIconOpacity + : kNormalIconOpacity; + q.setOpacity(progress + (1 - progress) * normal); + st::mediaviewTitleButtonMac.paint(q, 0, 0, raw->width()); + if (anyOver) { + q.setOpacity(1.); + current->paint(q, 0, 0, raw->width()); + } + q.end(); + }; + + raw->paintRequest( + ) | rpl::start_with_next([=, padding = info.padding] { + updateOver(); + prepareFrame(); + + auto p = QPainter(raw); + p.setOpacity(_data->masterOpacity.current()); + p.drawImage(padding.left(), padding.top(), state->frame); + }, raw->lifetime()); + + return result; +} + std::unique_ptr CreateOverlayWidgetHelper( not_null window, Fn maximize) { diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 7c293d3f3..de2fd25df 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -404,7 +404,7 @@ if customRunCommand: stage('patches', """ git clone https://github.com/desktop-app/patches.git cd patches - git checkout e8574ccccc + git checkout 53214c8a8a """) stage('msys64', """ diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 85b73e98e..0b87868b1 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 85b73e98eccec7e77d7910f92bf747c7db3f1c4c +Subproject commit 0b87868b1d708020155571d4e0c90095e58d6572