Fix calls build in Xcode. Fix calls panel in Retina.

Also implement panels that appear in all spaces on macOS.
Using them for calls panels and custom notifications, so it
will be possible to use custom notifications in macOS as well.
This commit is contained in:
John Preston 2017-05-02 10:25:20 +03:00
parent e050e270fc
commit 0cdac83f8a
17 changed files with 104 additions and 50 deletions

View file

@ -483,7 +483,7 @@ index 83c960d..03ae969 100755
}
@end
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 4d0458a..fde238a 100644
index 4d0458a..3357a5e 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -167,7 +167,8 @@ static bool isMouseEvent(NSEvent *ev)
@ -496,7 +496,24 @@ index 4d0458a..fde238a 100644
NSPoint loc = [theEvent locationInWindow];
NSRect windowFrame = [self.window legacyConvertRectFromScreen:[self.window frame]];
NSRect contentFrame = [[self.window contentView] frame];
@@ -914,6 +915,19 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath)
@@ -795,6 +796,16 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
{
Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
NSInteger styleMask = NSBorderlessWindowMask;
+
+ // Patch: allow creating panels floating on all spaces in macOS.
+ // If you call "setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary" before
+ // setting the "NSNonactivatingPanelMask" bit in the style mask it won't work after that.
+ // So we need a way to set that bit before Qt sets collection behavior the way it does.
+ QVariant nonactivatingPanelMask = window()->property("_td_macNonactivatingPanelMask");
+ if (nonactivatingPanelMask.isValid() && nonactivatingPanelMask.toBool()) {
+ styleMask |= NSNonactivatingPanelMask;
+ }
+
if (flags & Qt::FramelessWindowHint)
return styleMask;
if ((type & Qt::Popup) == Qt::Popup) {
@@ -914,6 +925,19 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath)
[m_nsWindow setRepresentedFilename: fi.exists() ? QCFString::toNSString(filePath) : @""];
}
@ -516,7 +533,7 @@ index 4d0458a..fde238a 100644
void QCocoaWindow::setWindowIcon(const QIcon &icon)
{
QCocoaAutoReleasePool pool;
@@ -929,7 +943,10 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon)
@@ -929,7 +953,10 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon)
if (icon.isNull()) {
[iconButton setImage:nil];
} else {

View file

@ -11505,7 +11505,7 @@ index 8152c57..5ddd7b3 100644
}
}
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index c0d5904..2991fca 100644
index c0d5904..f3c2047 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -141,7 +141,8 @@ static bool isMouseEvent(NSEvent *ev)
@ -11518,7 +11518,24 @@ index c0d5904..2991fca 100644
NSPoint loc = [theEvent locationInWindow];
NSRect windowFrame = [self.window convertRectFromScreen:[self.window frame]];
NSRect contentFrame = [[self.window contentView] frame];
@@ -943,6 +944,19 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath)
@@ -811,6 +812,16 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
{
Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
NSInteger styleMask = NSBorderlessWindowMask;
+
+ // Patch: allow creating panels floating on all spaces in macOS.
+ // If you call "setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary" before
+ // setting the "NSNonactivatingPanelMask" bit in the style mask it won't work after that.
+ // So we need a way to set that bit before Qt sets collection behavior the way it does.
+ QVariant nonactivatingPanelMask = window()->property("_td_macNonactivatingPanelMask");
+ if (nonactivatingPanelMask.isValid() && nonactivatingPanelMask.toBool()) {
+ styleMask |= NSNonactivatingPanelMask;
+ }
+
if (flags & Qt::FramelessWindowHint)
return styleMask;
if ((type & Qt::Popup) == Qt::Popup) {
@@ -943,6 +954,19 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath)
[m_nsWindow setRepresentedFilename: fi.exists() ? QCFString::toNSString(filePath) : @""];
}
@ -11538,7 +11555,7 @@ index c0d5904..2991fca 100644
void QCocoaWindow::setWindowIcon(const QIcon &icon)
{
QMacAutoReleasePool pool;
@@ -958,7 +972,9 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon)
@@ -958,7 +982,9 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon)
if (icon.isNull()) {
[iconButton setImage:nil];
} else {

View file

@ -46,10 +46,10 @@ public:
, _cache(cache) {
resize(cache.width() / cache.devicePixelRatio(), cache.height() / cache.devicePixelRatio());
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::Tool);
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
setAttribute(Qt::WA_TransparentForMouseEvents);
setAttribute(Qt::WA_OpaquePaintEvent);
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint);
setWindowOpacity(0.);
show();

View file

@ -113,16 +113,16 @@ BoxController::Row::Row(HistoryItem *item) : PeerListBox::Row(item->history()->p
}
void BoxController::Row::paintStatusText(Painter &p, int x, int y, int outerWidth, bool selected) {
auto &icon = ([this] {
auto icon = ([this] {
switch (_type) {
case Type::In: return st::callArrowIn;
case Type::Out: return st::callArrowOut;
case Type::Missed: return st::callArrowMissed;
case Type::In: return &st::callArrowIn;
case Type::Out: return &st::callArrowOut;
case Type::Missed: return &st::callArrowMissed;
}
Unexpected("_type in Calls::BoxController::Row::paintStatusText().");
})();
icon.paint(p, x + st::callArrowPosition.x(), y + st::callArrowPosition.y(), outerWidth);
x += + st::callArrowPosition.x() + icon.width() + st::callArrowSkip;
icon->paint(p, x + st::callArrowPosition.x(), y + st::callArrowPosition.y(), outerWidth);
x += + st::callArrowPosition.x() + icon->width() + st::callArrowSkip;
PeerListBox::Row::paintStatusText(p, x, y, outerWidth, selected);
}
@ -285,7 +285,7 @@ bool BoxController::insertRow(HistoryItem *item, InsertWay way) {
}
}
(way == InsertWay::Append) ? view()->appendRow(createRow(item)) : view()->prependRow(createRow(item));
view()->reorderRows([](auto &begin, auto &end) {
view()->reorderRows([](auto &&begin, auto &&end) {
std::sort(begin, end, [](auto &a, auto &b) {
return static_cast<Row&>(*a).maxItemId() > static_cast<Row&>(*a).maxItemId();
});

View file

@ -184,8 +184,6 @@ void Panel::refreshCallbacks() {
}
void Panel::initLayout() {
hide();
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::Tool);
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
setAttribute(Qt::WA_NoSystemBackground, true);
@ -203,6 +201,8 @@ void Panel::initLayout() {
refreshUserPhoto();
});
createDefaultCacheImage();
Platform::InitOnTopPanel(this);
}
void Panel::processUserPhoto() {
@ -235,7 +235,7 @@ void Panel::refreshUserPhoto() {
void Panel::createUserpicCache(ImagePtr image) {
auto size = st::callWidth * cIntRetinaFactor();
auto options = _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : 0;
auto options = _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : Images::Option::None;
auto width = image->width();
auto height = image->height();
if (width > height) {
@ -245,7 +245,7 @@ void Panel::createUserpicCache(ImagePtr image) {
height = qMax((height * size) / width, 1);
width = size;
}
_userPhoto = image->pixNoCache(width, height, options, size, size);
_userPhoto = image->pixNoCache(width, height, options, st::callWidth, st::callWidth);
if (cRetina()) _userPhoto.setDevicePixelRatio(cRetinaFactor());
refreshCacheImageUserPhoto();
@ -301,7 +301,8 @@ void Panel::createDefaultCacheImage() {
if (!_useTransparency || !_cache.isNull()) {
return;
}
auto cache = QImage(size(), QImage::Format_ARGB32_Premultiplied);
auto cache = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
cache.setDevicePixelRatio(cRetinaFactor());
cache.fill(Qt::transparent);
{
Painter p(&cache);
@ -317,7 +318,8 @@ void Panel::createDefaultCacheImage() {
}
void Panel::refreshCacheImageUserPhoto() {
auto cache = QImage(size(), QImage::Format_ARGB32_Premultiplied);
auto cache = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
cache.setDevicePixelRatio(cRetinaFactor());
cache.fill(Qt::transparent);
{
Painter p(&cache);

View file

@ -45,11 +45,9 @@ void TopBar::initControls() {
_mute->setClickedCallback([this] {
_call->setMute(!_call->isMute());
});
setMuted(_call->isMute());
subscribe(_call->muteChanged(), [this](bool mute) {
_mute->setIconOverride(mute ? &st::callBarUnmuteIcon : nullptr);
_mute->setRippleColorOverride(mute ? &st::callBarUnmuteRipple : nullptr);
_hangup->setRippleColorOverride(mute ? &st::callBarUnmuteRipple : nullptr);
_muted = mute;
setMuted(mute);
update();
});
_info->setClickedCallback([this] {
@ -66,6 +64,13 @@ void TopBar::initControls() {
updateDurationText();
}
void TopBar::setMuted(bool mute) {
_mute->setIconOverride(mute ? &st::callBarUnmuteIcon : nullptr);
_mute->setRippleColorOverride(mute ? &st::callBarUnmuteRipple : nullptr);
_hangup->setRippleColorOverride(mute ? &st::callBarUnmuteRipple : nullptr);
_muted = mute;
}
void TopBar::updateDurationText() {
if (!_call) {
return;

View file

@ -48,6 +48,7 @@ private:
void updateDurationText();
void updateControlsGeometry();
void startDurationUpdateTimer(TimeMs currentDuration);
void setMuted(bool mute);
base::weak_unique_ptr<Call> _call;

View file

@ -25,9 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Platform {
namespace Notifications {
inline void CustomNotificationShownHook(QWidget *widget) {
}
inline bool SkipAudio() {
return false;
}

View file

@ -405,6 +405,9 @@ bool TransparentWindowsSupported(QPoint globalPosition) {
return false;
}
void InitOnTopPanel(QWidget *panel) {
}
namespace ThirdParty {
void start() {

View file

@ -168,16 +168,6 @@ void FlashBounce() {
[NSApp requestUserAttention:NSInformationalRequest];
}
void CustomNotificationShownHook(QWidget *widget) {
widget->hide();
objc_holdOnTop(widget->winId());
widget->show();
psShowOverAll(widget, false);
if (auto window = App::wnd()) {
window->customNotificationCreated(widget);
}
}
class Manager::Private : public QObject, private base::Subscriber {
public:
Private(Manager *manager);

View file

@ -20,7 +20,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
// e is NSEvent*
bool objc_handleMediaKeyEvent(void *e);
void objc_holdOnTop(WId winId);
bool objc_darkMode();
void objc_showOverAll(WId winId, bool canFocus = true);
void objc_bringToBack(WId winId);

View file

@ -36,7 +36,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace {
constexpr auto kIgnoreActivationTimeoutMs = 1500;
constexpr auto kIgnoreActivationTimeoutMs = 500;
} // namespace
@ -196,13 +196,34 @@ void SetWatchingMediaKeys(bool watching) {
}
}
} // namespace Platform
void InitOnTopPanel(QWidget *panel) {
Expects(!panel->windowHandle());
void objc_holdOnTop(WId winId) {
NSWindow *wnd = [reinterpret_cast<NSView *>(winId) window];
[wnd setHidesOnDeactivate:NO];
// Force creating windowHandle() without creating the platform window yet.
panel->setAttribute(Qt::WA_NativeWindow, true);
panel->windowHandle()->setProperty("_td_macNonactivatingPanelMask", QVariant(true));
panel->setAttribute(Qt::WA_NativeWindow, false);
panel->createWinId();
auto platformWindow = [reinterpret_cast<NSView*>(panel->winId()) window];
t_assert([platformWindow isKindOfClass:[NSPanel class]]);
auto platformPanel = static_cast<NSPanel*>(platformWindow);
[platformPanel setLevel:NSPopUpMenuWindowLevel];
[platformPanel setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorStationary|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle];
[platformPanel setFloatingPanel:YES];
[platformPanel setHidesOnDeactivate:NO];
if (auto window = App::wnd()) {
window->customNotificationCreated(panel);
}
objc_ignoreApplicationActivationRightNow();
}
} // namespace Platform
bool objc_darkMode() {
bool result = false;
@autoreleasepool {

View file

@ -25,7 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Platform {
namespace Notifications {
void CustomNotificationShownHook(QWidget *widget);
bool SkipAudio();
bool SkipToast();

View file

@ -36,6 +36,8 @@ void finish();
void SetWatchingMediaKeys(bool watching);
bool TransparentWindowsSupported(QPoint globalPosition);
void InitOnTopPanel(QWidget *panel);
namespace ThirdParty {
void start();

View file

@ -25,9 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Platform {
namespace Notifications {
inline void CustomNotificationShownHook(QWidget *widget) {
}
class Manager : public Window::Notifications::NativeManager {
public:
Manager(Window::Notifications::System *system);

View file

@ -534,6 +534,9 @@ bool TransparentWindowsSupported(QPoint globalPosition) {
return true;
}
void InitOnTopPanel(QWidget *panel) {
}
namespace ThirdParty {
void start() {

View file

@ -199,7 +199,6 @@ void Manager::showNextFromQueue() {
queued.item,
queued.forwardedCount,
startPosition, startShift, shiftDirection);
Platform::Notifications::CustomNotificationShownHook(notification.get());
_notifications.push_back(std::move(notification));
--count;
} while (count > 0 && !_queuedNotifications.empty());
@ -353,9 +352,11 @@ Widget::Widget(Manager *manager, QPoint startPosition, int shift, Direction shif
, _a_shift(animation(this, &Widget::step_shift)) {
setWindowOpacity(0.);
setWindowFlags(qFlags(Qt::Tool) | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_OpaquePaintEvent);
setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::Tool);
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
setAttribute(Qt::WA_OpaquePaintEvent);
Platform::InitOnTopPanel(this);
_a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::notifyFastAnim);
}