mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-07-25 16:58:40 +08:00
refactor: rebuild desktop client with Slint
This commit is contained in:
@@ -190,6 +190,18 @@ struct LinuxTrayImpl {
|
||||
menu_label(GetMenuLabel(language_index_value)),
|
||||
menu_ascii_label(IsAsciiPrintable(menu_label) ? menu_label : "Exit") {}
|
||||
|
||||
explicit LinuxTrayImpl(std::function<void()> show_window_callback,
|
||||
std::function<void()> hide_window_callback,
|
||||
std::function<void()> exit_callback,
|
||||
std::string tray_tooltip, int language_index_value)
|
||||
: show_window(std::move(show_window_callback)),
|
||||
hide_window(std::move(hide_window_callback)),
|
||||
exit_app(std::move(exit_callback)),
|
||||
tooltip(std::move(tray_tooltip)),
|
||||
language_index(language_index_value),
|
||||
menu_label(GetMenuLabel(language_index_value)),
|
||||
menu_ascii_label(IsAsciiPrintable(menu_label) ? menu_label : "Exit") {}
|
||||
|
||||
~LinuxTrayImpl() { RemoveTrayIcon(); }
|
||||
|
||||
bool MinimizeToTray() {
|
||||
@@ -197,7 +209,9 @@ struct LinuxTrayImpl {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (app_window) {
|
||||
if (hide_window) {
|
||||
hide_window();
|
||||
} else if (app_window) {
|
||||
SDL_HideWindow(app_window);
|
||||
}
|
||||
return true;
|
||||
@@ -939,16 +953,20 @@ struct LinuxTrayImpl {
|
||||
}
|
||||
|
||||
void ShowWindow() {
|
||||
if (!app_window) {
|
||||
return;
|
||||
if (show_window) {
|
||||
show_window();
|
||||
} else if (app_window) {
|
||||
SDL_ShowWindow(app_window);
|
||||
SDL_RestoreWindow(app_window);
|
||||
SDL_RaiseWindow(app_window);
|
||||
}
|
||||
|
||||
SDL_ShowWindow(app_window);
|
||||
SDL_RestoreWindow(app_window);
|
||||
SDL_RaiseWindow(app_window);
|
||||
}
|
||||
|
||||
void RequestExit() {
|
||||
if (exit_app) {
|
||||
exit_app();
|
||||
return;
|
||||
}
|
||||
SDL_Event event{};
|
||||
event.type =
|
||||
exit_event_type == 0 || exit_event_type == static_cast<uint32_t>(-1)
|
||||
@@ -958,6 +976,9 @@ struct LinuxTrayImpl {
|
||||
}
|
||||
|
||||
::SDL_Window* app_window = nullptr;
|
||||
std::function<void()> show_window;
|
||||
std::function<void()> hide_window;
|
||||
std::function<void()> exit_app;
|
||||
std::string tooltip;
|
||||
int language_index = 0;
|
||||
uint32_t exit_event_type = 0;
|
||||
@@ -1005,6 +1026,14 @@ LinuxTray::LinuxTray(::SDL_Window* app_window, const std::string& tooltip,
|
||||
: impl_(std::make_unique<LinuxTrayImpl>(app_window, tooltip, language_index,
|
||||
exit_event_type)) {}
|
||||
|
||||
LinuxTray::LinuxTray(std::function<void()> show_window,
|
||||
std::function<void()> hide_window,
|
||||
std::function<void()> exit_app,
|
||||
const std::string& tooltip, int language_index)
|
||||
: impl_(std::make_unique<LinuxTrayImpl>(
|
||||
std::move(show_window), std::move(hide_window), std::move(exit_app),
|
||||
tooltip, language_index)) {}
|
||||
|
||||
LinuxTray::~LinuxTray() = default;
|
||||
|
||||
bool LinuxTray::MinimizeToTray() { return impl_->MinimizeToTray(); }
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#if defined(__linux__) && !defined(__APPLE__)
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -23,6 +24,9 @@ class LinuxTray {
|
||||
public:
|
||||
LinuxTray(::SDL_Window* app_window, const std::string& tooltip,
|
||||
int language_index, uint32_t exit_event_type);
|
||||
LinuxTray(std::function<void()> show_window,
|
||||
std::function<void()> hide_window, std::function<void()> exit_app,
|
||||
const std::string& tooltip, int language_index);
|
||||
~LinuxTray();
|
||||
|
||||
bool MinimizeToTray();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef _MAC_TRAY_H_
|
||||
#define _MAC_TRAY_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -20,6 +21,11 @@ class MacTray {
|
||||
public:
|
||||
MacTray(::SDL_Window* app_window, const std::string& tooltip,
|
||||
int language_index);
|
||||
MacTray(std::function<void()> show_window,
|
||||
std::function<void()> hide_window,
|
||||
std::function<void()> open_settings,
|
||||
std::function<void()> exit_app,
|
||||
const std::string& tooltip, int language_index);
|
||||
~MacTray();
|
||||
|
||||
void MinimizeToTray();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
@interface CrossDeskMacTrayTarget : NSObject
|
||||
- (instancetype)initWithOwner:(crossdesk::MacTrayImpl *)owner;
|
||||
- (void)statusItemClicked:(id)sender;
|
||||
- (void)openSettings:(id)sender;
|
||||
- (void)exitApplication:(id)sender;
|
||||
@end
|
||||
|
||||
@@ -24,7 +25,24 @@ struct MacTrayImpl {
|
||||
: app_window(window),
|
||||
tooltip(std::move(tray_tooltip)),
|
||||
language_index(language_index_value),
|
||||
target([[CrossDeskMacTrayTarget alloc] initWithOwner:this]) {}
|
||||
target([[CrossDeskMacTrayTarget alloc] initWithOwner:this]) {
|
||||
EnsureStatusItem();
|
||||
}
|
||||
|
||||
explicit MacTrayImpl(std::function<void()> show_window_callback,
|
||||
std::function<void()> hide_window_callback,
|
||||
std::function<void()> open_settings_callback,
|
||||
std::function<void()> exit_callback,
|
||||
std::string tray_tooltip, int language_index_value)
|
||||
: show_window(std::move(show_window_callback)),
|
||||
hide_window(std::move(hide_window_callback)),
|
||||
open_settings(std::move(open_settings_callback)),
|
||||
exit_app(std::move(exit_callback)),
|
||||
tooltip(std::move(tray_tooltip)),
|
||||
language_index(language_index_value),
|
||||
target([[CrossDeskMacTrayTarget alloc] initWithOwner:this]) {
|
||||
EnsureStatusItem();
|
||||
}
|
||||
|
||||
~MacTrayImpl() {
|
||||
RemoveTrayIcon();
|
||||
@@ -33,7 +51,9 @@ struct MacTrayImpl {
|
||||
|
||||
void MinimizeToTray() {
|
||||
EnsureStatusItem();
|
||||
if (app_window) {
|
||||
if (hide_window) {
|
||||
hide_window();
|
||||
} else if (app_window) {
|
||||
SDL_HideWindow(app_window);
|
||||
}
|
||||
}
|
||||
@@ -48,12 +68,12 @@ struct MacTrayImpl {
|
||||
}
|
||||
|
||||
void ShowWindow() {
|
||||
if (!app_window) {
|
||||
return;
|
||||
if (show_window) {
|
||||
show_window();
|
||||
} else if (app_window) {
|
||||
SDL_ShowWindow(app_window);
|
||||
SDL_RaiseWindow(app_window);
|
||||
}
|
||||
|
||||
SDL_ShowWindow(app_window);
|
||||
SDL_RaiseWindow(app_window);
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
@@ -64,6 +84,18 @@ struct MacTrayImpl {
|
||||
}
|
||||
|
||||
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"CrossDesk"];
|
||||
NSString *settings_title =
|
||||
NSStringFromUtf8(localization::settings
|
||||
[localization::detail::ClampLanguageIndex(
|
||||
language_index)]);
|
||||
NSMenuItem *settings_item =
|
||||
[[NSMenuItem alloc] initWithTitle:settings_title
|
||||
action:@selector(openSettings:)
|
||||
keyEquivalent:@""];
|
||||
[settings_item setTarget:target];
|
||||
[menu addItem:settings_item];
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
NSString *exit_title =
|
||||
NSStringFromUtf8(localization::exit_program
|
||||
[localization::detail::ClampLanguageIndex(
|
||||
@@ -86,11 +118,25 @@ struct MacTrayImpl {
|
||||
}
|
||||
|
||||
void RequestExit() {
|
||||
if (exit_app) {
|
||||
exit_app();
|
||||
return;
|
||||
}
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
|
||||
void OpenSettings() {
|
||||
if (open_settings) {
|
||||
open_settings();
|
||||
} else {
|
||||
ShowWindow();
|
||||
return;
|
||||
}
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
private:
|
||||
void EnsureStatusItem() {
|
||||
if (status_item) {
|
||||
@@ -191,6 +237,10 @@ struct MacTrayImpl {
|
||||
}
|
||||
|
||||
::SDL_Window *app_window = nullptr;
|
||||
std::function<void()> show_window;
|
||||
std::function<void()> hide_window;
|
||||
std::function<void()> open_settings;
|
||||
std::function<void()> exit_app;
|
||||
std::string tooltip;
|
||||
int language_index = 0;
|
||||
NSStatusItem *status_item = nil;
|
||||
@@ -202,6 +252,16 @@ MacTray::MacTray(::SDL_Window *app_window, const std::string &tooltip,
|
||||
: impl_(
|
||||
std::make_unique<MacTrayImpl>(app_window, tooltip, language_index)) {}
|
||||
|
||||
MacTray::MacTray(std::function<void()> show_window,
|
||||
std::function<void()> hide_window,
|
||||
std::function<void()> open_settings,
|
||||
std::function<void()> exit_app, const std::string &tooltip,
|
||||
int language_index)
|
||||
: impl_(std::make_unique<MacTrayImpl>(
|
||||
std::move(show_window), std::move(hide_window),
|
||||
std::move(open_settings), std::move(exit_app), tooltip,
|
||||
language_index)) {}
|
||||
|
||||
MacTray::~MacTray() = default;
|
||||
|
||||
void MacTray::MinimizeToTray() { impl_->MinimizeToTray(); }
|
||||
@@ -244,6 +304,13 @@ void MacTray::RemoveTrayIcon() { impl_->RemoveTrayIcon(); }
|
||||
}
|
||||
}
|
||||
|
||||
- (void)openSettings:(id)sender {
|
||||
(void)sender;
|
||||
if (owner_) {
|
||||
owner_->OpenSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "localization.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
// callback for the message-only window that handles tray icon messages
|
||||
@@ -60,6 +62,16 @@ WinTray::WinTray(HWND app_hwnd, HICON icon, const std::wstring& tooltip,
|
||||
wcsncpy_s(nid_.szTip, tip_.c_str(), _TRUNCATE);
|
||||
}
|
||||
|
||||
WinTray::WinTray(std::function<void()> show_window,
|
||||
std::function<void()> hide_window,
|
||||
std::function<void()> exit_app, HICON icon,
|
||||
const std::wstring& tooltip, int language_index)
|
||||
: WinTray(nullptr, icon, tooltip, language_index) {
|
||||
show_window_ = std::move(show_window);
|
||||
hide_window_ = std::move(hide_window);
|
||||
exit_app_ = std::move(exit_app);
|
||||
}
|
||||
|
||||
WinTray::~WinTray() {
|
||||
RemoveTrayIcon();
|
||||
if (hwnd_message_only_) DestroyWindow(hwnd_message_only_);
|
||||
@@ -67,20 +79,31 @@ WinTray::~WinTray() {
|
||||
|
||||
void WinTray::MinimizeToTray() {
|
||||
Shell_NotifyIcon(NIM_ADD, &nid_);
|
||||
// hide application window
|
||||
ShowWindow(app_hwnd_, SW_HIDE);
|
||||
if (hide_window_) {
|
||||
hide_window_();
|
||||
} else if (app_hwnd_) {
|
||||
ShowWindow(app_hwnd_, SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
void WinTray::RemoveTrayIcon() { Shell_NotifyIcon(NIM_DELETE, &nid_); }
|
||||
|
||||
void WinTray::ShowApplicationWindow() {
|
||||
if (show_window_) {
|
||||
show_window_();
|
||||
} else if (app_hwnd_) {
|
||||
ShowWindow(app_hwnd_, SW_SHOW);
|
||||
SetForegroundWindow(app_hwnd_);
|
||||
}
|
||||
}
|
||||
|
||||
bool WinTray::HandleTrayMessage(MSG* msg) {
|
||||
if (!msg || msg->message != WM_TRAY_CALLBACK) return false;
|
||||
|
||||
switch (LOWORD(msg->lParam)) {
|
||||
case WM_LBUTTONDBLCLK:
|
||||
case WM_LBUTTONUP: {
|
||||
ShowWindow(app_hwnd_, SW_SHOW);
|
||||
SetForegroundWindow(app_hwnd_);
|
||||
ShowApplicationWindow();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -99,13 +122,15 @@ bool WinTray::HandleTrayMessage(MSG* msg) {
|
||||
|
||||
// handle menu command
|
||||
if (cmd == 1001) {
|
||||
// exit application
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
if (exit_app_) {
|
||||
exit_app_();
|
||||
} else {
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
} else if (cmd == 1002) {
|
||||
ShowWindow(app_hwnd_, SW_SHOW); // show main window
|
||||
SetForegroundWindow(app_hwnd_);
|
||||
ShowApplicationWindow();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#define WM_TRAY_CALLBACK (WM_USER + 1)
|
||||
@@ -20,6 +21,9 @@ class WinTray {
|
||||
public:
|
||||
WinTray(HWND app_hwnd, HICON icon, const std::wstring& tooltip,
|
||||
int language_index);
|
||||
WinTray(std::function<void()> show_window,
|
||||
std::function<void()> hide_window, std::function<void()> exit_app,
|
||||
HICON icon, const std::wstring& tooltip, int language_index);
|
||||
~WinTray();
|
||||
|
||||
void MinimizeToTray();
|
||||
@@ -27,12 +31,17 @@ class WinTray {
|
||||
bool HandleTrayMessage(MSG* msg);
|
||||
|
||||
private:
|
||||
void ShowApplicationWindow();
|
||||
|
||||
HWND app_hwnd_;
|
||||
HWND hwnd_message_only_;
|
||||
HICON icon_;
|
||||
std::wstring tip_;
|
||||
int language_index_;
|
||||
NOTIFYICONDATA nid_;
|
||||
std::function<void()> show_window_;
|
||||
std::function<void()> hide_window_;
|
||||
std::function<void()> exit_app_;
|
||||
};
|
||||
} // namespace crossdesk
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
// Starts the platform's native move loop for the window receiving the current
|
||||
// left-mouse-down event. Returns false when no suitable native event exists.
|
||||
bool StartNativeWindowDrag();
|
||||
|
||||
// Hides the disabled native zoom button on CrossDesk's fixed-size main
|
||||
// window. Returns true once the matching AppKit window has been configured.
|
||||
bool HideDisabledMainWindowZoomButton();
|
||||
|
||||
// Configures live resize and replaces the stream window's native Space
|
||||
// fullscreen action with an immediate, single-window fullscreen transition.
|
||||
bool ConfigureStreamWindowLiveResize();
|
||||
|
||||
// Enters or leaves the stream window's animation-free fullscreen mode.
|
||||
bool SetStreamWindowFullscreen(bool fullscreen);
|
||||
bool IsStreamWindowFullscreen();
|
||||
|
||||
// Opens an application-modal macOS file picker. The panel is made key before
|
||||
// entering its modal loop so it receives keyboard input immediately.
|
||||
std::string OpenNativeFileDialog(const std::string &title);
|
||||
|
||||
} // namespace crossdesk
|
||||
@@ -0,0 +1,265 @@
|
||||
#include "platform/window_drag.h"
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@interface CrossDeskStreamFullscreenTarget : NSObject
|
||||
- (void)toggleStreamFullscreen:(id)sender;
|
||||
@end
|
||||
|
||||
@implementation CrossDeskStreamFullscreenTarget
|
||||
- (void)toggleStreamFullscreen:(id)sender {
|
||||
(void)sender;
|
||||
crossdesk::SetStreamWindowFullscreen(
|
||||
!crossdesk::IsStreamWindowFullscreen());
|
||||
}
|
||||
@end
|
||||
|
||||
namespace crossdesk {
|
||||
namespace {
|
||||
|
||||
NSWindow *stream_window = nil;
|
||||
CrossDeskStreamFullscreenTarget *stream_fullscreen_target = nil;
|
||||
bool stream_fullscreen = false;
|
||||
NSRect stream_restore_frame = NSZeroRect;
|
||||
NSWindowStyleMask stream_restore_style_mask = NSWindowStyleMaskTitled;
|
||||
NSWindowTitleVisibility stream_restore_title_visibility =
|
||||
NSWindowTitleVisible;
|
||||
BOOL stream_restore_titlebar_transparent = NO;
|
||||
BOOL stream_restore_movable = YES;
|
||||
BOOL stream_restore_close_hidden = NO;
|
||||
BOOL stream_restore_miniaturize_hidden = NO;
|
||||
BOOL stream_restore_zoom_hidden = NO;
|
||||
NSApplicationPresentationOptions stream_restore_presentation_options =
|
||||
NSApplicationPresentationDefault;
|
||||
|
||||
void InstallStreamFullscreenButton(NSWindow *window) {
|
||||
if (window == nil) {
|
||||
return;
|
||||
}
|
||||
if (stream_fullscreen_target == nil) {
|
||||
stream_fullscreen_target =
|
||||
[[CrossDeskStreamFullscreenTarget alloc] init];
|
||||
}
|
||||
NSButton *zoom_button =
|
||||
[window standardWindowButton:NSWindowZoomButton];
|
||||
zoom_button.target = stream_fullscreen_target;
|
||||
zoom_button.action = @selector(toggleStreamFullscreen:);
|
||||
}
|
||||
|
||||
void SetTitlebarButtonsHidden(NSWindow *window, BOOL hidden) {
|
||||
[window standardWindowButton:NSWindowCloseButton].hidden = hidden;
|
||||
[window standardWindowButton:NSWindowMiniaturizeButton].hidden = hidden;
|
||||
[window standardWindowButton:NSWindowZoomButton].hidden = hidden;
|
||||
}
|
||||
|
||||
void CenterWindowOnScreen(NSWindow *window, NSScreen *screen) {
|
||||
if (window == nil || screen == nil) {
|
||||
return;
|
||||
}
|
||||
const NSRect available_frame = screen.visibleFrame;
|
||||
const NSRect window_frame = window.frame;
|
||||
const NSPoint centered_origin = NSMakePoint(
|
||||
NSMidX(available_frame) - NSWidth(window_frame) * 0.5,
|
||||
NSMidY(available_frame) - NSHeight(window_frame) * 0.5);
|
||||
[window setFrameOrigin:centered_origin];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool StartNativeWindowDrag() {
|
||||
@autoreleasepool {
|
||||
NSEvent *event = [[NSApplication sharedApplication] currentEvent];
|
||||
if (event == nil || event.type != NSEventTypeLeftMouseDown ||
|
||||
event.window == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
[event.window performWindowDragWithEvent:event];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool HideDisabledMainWindowZoomButton() {
|
||||
@autoreleasepool {
|
||||
for (NSWindow *window in [NSApp windows]) {
|
||||
if (![window.title isEqualToString:@"CrossDesk"]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSButton *zoom_button =
|
||||
[window standardWindowButton:NSWindowZoomButton];
|
||||
// The main window has a fixed size, so AppKit disables its zoom button.
|
||||
// Stream windows remain resizable and must keep their native button.
|
||||
if (zoom_button == nil || zoom_button.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
zoom_button.hidden = YES;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConfigureStreamWindowLiveResize() {
|
||||
@autoreleasepool {
|
||||
for (NSWindow *window in [NSApp windows]) {
|
||||
if (![window.title isEqualToString:@"CrossDesk"]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSButton *zoom_button =
|
||||
[window standardWindowButton:NSWindowZoomButton];
|
||||
// The stream window is resizable and therefore keeps an enabled native
|
||||
// zoom button. The fixed-size main window's zoom button is disabled.
|
||||
if (zoom_button == nil || !zoom_button.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSView *content_view = window.contentView;
|
||||
if (content_view == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// During an ordinary edge resize, scale one cached frame and redraw once
|
||||
// the new window size has settled.
|
||||
window.preservesContentDuringLiveResize = YES;
|
||||
content_view.layerContentsRedrawPolicy =
|
||||
NSViewLayerContentsRedrawBeforeViewResize;
|
||||
content_view.layerContentsPlacement =
|
||||
NSViewLayerContentsPlacementScaleProportionallyToFit;
|
||||
stream_window = window;
|
||||
// Native Space fullscreen cross-fades an AppKit source snapshot over a
|
||||
// separately rendered destination window. Disable that path for the
|
||||
// stream window and route the green button to the immediate transition.
|
||||
window.collectionBehavior =
|
||||
(window.collectionBehavior &
|
||||
~(NSWindowCollectionBehaviorFullScreenPrimary |
|
||||
NSWindowCollectionBehaviorFullScreenAuxiliary |
|
||||
NSWindowCollectionBehaviorFullScreenAllowsTiling)) |
|
||||
NSWindowCollectionBehaviorFullScreenNone;
|
||||
InstallStreamFullscreenButton(window);
|
||||
content_view.needsDisplay = YES;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SetStreamWindowFullscreen(bool fullscreen) {
|
||||
@autoreleasepool {
|
||||
if (stream_window == nil || stream_fullscreen == fullscreen) {
|
||||
return stream_window != nil;
|
||||
}
|
||||
|
||||
if (fullscreen) {
|
||||
NSScreen *screen = stream_window.screen ?: NSScreen.mainScreen;
|
||||
if (screen == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream_restore_frame = stream_window.frame;
|
||||
stream_restore_style_mask = stream_window.styleMask;
|
||||
stream_restore_title_visibility = stream_window.titleVisibility;
|
||||
stream_restore_titlebar_transparent =
|
||||
stream_window.titlebarAppearsTransparent;
|
||||
stream_restore_movable = stream_window.movable;
|
||||
stream_restore_close_hidden =
|
||||
[stream_window standardWindowButton:NSWindowCloseButton].hidden;
|
||||
stream_restore_miniaturize_hidden =
|
||||
[stream_window standardWindowButton:NSWindowMiniaturizeButton].hidden;
|
||||
stream_restore_zoom_hidden =
|
||||
[stream_window standardWindowButton:NSWindowZoomButton].hidden;
|
||||
stream_restore_presentation_options = NSApp.presentationOptions;
|
||||
|
||||
stream_fullscreen = true;
|
||||
stream_window.styleMask =
|
||||
stream_restore_style_mask | NSWindowStyleMaskFullSizeContentView;
|
||||
stream_window.titleVisibility = NSWindowTitleHidden;
|
||||
stream_window.titlebarAppearsTransparent = YES;
|
||||
stream_window.movable = NO;
|
||||
SetTitlebarButtonsHidden(stream_window, YES);
|
||||
|
||||
NSApplicationPresentationOptions presentation_options =
|
||||
stream_restore_presentation_options;
|
||||
if ((presentation_options & (NSApplicationPresentationHideDock |
|
||||
NSApplicationPresentationAutoHideDock)) ==
|
||||
0) {
|
||||
presentation_options |= NSApplicationPresentationAutoHideDock;
|
||||
}
|
||||
if ((presentation_options & (NSApplicationPresentationHideMenuBar |
|
||||
NSApplicationPresentationAutoHideMenuBar)) ==
|
||||
0) {
|
||||
presentation_options |= NSApplicationPresentationAutoHideMenuBar;
|
||||
}
|
||||
NSApp.presentationOptions = presentation_options;
|
||||
[stream_window setFrame:screen.frame display:YES animate:NO];
|
||||
[stream_window makeKeyAndOrderFront:nil];
|
||||
} else {
|
||||
stream_fullscreen = false;
|
||||
NSApp.presentationOptions = stream_restore_presentation_options;
|
||||
stream_window.styleMask = stream_restore_style_mask;
|
||||
stream_window.titleVisibility = stream_restore_title_visibility;
|
||||
stream_window.titlebarAppearsTransparent =
|
||||
stream_restore_titlebar_transparent;
|
||||
stream_window.movable = stream_restore_movable;
|
||||
[stream_window setFrame:stream_restore_frame display:YES animate:NO];
|
||||
[stream_window standardWindowButton:NSWindowCloseButton].hidden =
|
||||
stream_restore_close_hidden;
|
||||
[stream_window standardWindowButton:NSWindowMiniaturizeButton].hidden =
|
||||
stream_restore_miniaturize_hidden;
|
||||
[stream_window standardWindowButton:NSWindowZoomButton].hidden =
|
||||
stream_restore_zoom_hidden;
|
||||
InstallStreamFullscreenButton(stream_window);
|
||||
[stream_window makeKeyAndOrderFront:nil];
|
||||
}
|
||||
|
||||
stream_window.contentView.alphaValue = 1.0;
|
||||
stream_window.contentView.needsDisplay = YES;
|
||||
[stream_window displayIfNeeded];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsStreamWindowFullscreen() {
|
||||
return stream_fullscreen;
|
||||
}
|
||||
|
||||
std::string OpenNativeFileDialog(const std::string &title) {
|
||||
@autoreleasepool {
|
||||
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
||||
panel.canChooseFiles = YES;
|
||||
panel.canChooseDirectories = NO;
|
||||
panel.allowsMultipleSelection = NO;
|
||||
panel.resolvesAliases = YES;
|
||||
|
||||
NSString *prompt =
|
||||
[[NSString alloc] initWithBytes:title.data()
|
||||
length:title.size()
|
||||
encoding:NSUTF8StringEncoding];
|
||||
if (prompt != nil && prompt.length > 0) {
|
||||
panel.title = prompt;
|
||||
panel.message = prompt;
|
||||
}
|
||||
|
||||
// tinyfiledialogs uses an external osascript process on macOS, so its
|
||||
// chooser can appear without becoming the active window. Keep the picker
|
||||
// inside CrossDesk and explicitly activate both the app and panel.
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
NSWindow *owner = stream_window ?: NSApp.keyWindow ?: NSApp.mainWindow;
|
||||
if (owner != nil) {
|
||||
[owner makeKeyAndOrderFront:nil];
|
||||
}
|
||||
NSScreen *target_screen = owner.screen ?: NSScreen.mainScreen;
|
||||
CenterWindowOnScreen(panel, target_screen);
|
||||
[panel makeKeyAndOrderFront:nil];
|
||||
|
||||
if ([panel runModal] != NSModalResponseOK || panel.URL == nil) {
|
||||
return {};
|
||||
}
|
||||
const char *path = panel.URL.fileSystemRepresentation;
|
||||
return path != nullptr ? std::string(path) : std::string{};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
Reference in New Issue
Block a user