mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-11-06 14:09:09 +08:00
[fix] position windows before showing to prevent flicker
This commit is contained in:
@@ -97,6 +97,7 @@
|
||||
#include "updater/update_controller.h"
|
||||
#endif
|
||||
#include "window_geometry.h"
|
||||
#include "window_presentation.h"
|
||||
|
||||
namespace crossdesk {
|
||||
namespace {
|
||||
@@ -1116,9 +1117,6 @@ struct GuiApplication::SlintUi {
|
||||
bool server_file_dialog_open = false;
|
||||
bool server_suspended_for_dialog = false;
|
||||
server_window_state::EdgeLayout server_layout{};
|
||||
#if _WIN32
|
||||
int server_native_window_attempts = 0;
|
||||
#endif
|
||||
int localized_language = -1;
|
||||
std::chrono::steady_clock::time_point last_clipboard_poll{};
|
||||
slint::Timer video_timer;
|
||||
@@ -3764,9 +3762,10 @@ void GuiApplication::SyncStreamWindow() {
|
||||
(*ui_->stream)->set_displays(ui_->display_model);
|
||||
(*ui_->stream)->set_stats_rows(ui_->stats_model);
|
||||
BindStreamCallbacks();
|
||||
(*ui_->stream)->show();
|
||||
// Repeat after the next layout pass because the backend resolves the
|
||||
// initial DPI and preferred size asynchronously on some platforms.
|
||||
(*ui_->stream)->window().set_size(slint::LogicalSize({
|
||||
kStreamWindowLogicalWidth,
|
||||
StreamWindowLogicalHeight((*ui_->stream)->get_custom_titlebar())}));
|
||||
// Resolve placement and any display-DPI change while still hidden.
|
||||
ui_->stream_initial_position_attempts = 2;
|
||||
#if defined(__APPLE__)
|
||||
ui_->stream_live_resize_configuration_attempts = 30;
|
||||
@@ -3781,8 +3780,10 @@ void GuiApplication::SyncStreamWindow() {
|
||||
// A wall takeover hides an existing stream window without destroying it.
|
||||
// Opening a recent device must also restore that existing window.
|
||||
if (need_to_create_stream_window_) {
|
||||
(*ui_->stream)->window().set_minimized(false);
|
||||
(*ui_->stream)->show();
|
||||
if (ui_->stream_initial_position_attempts == 0) {
|
||||
(*ui_->stream)->window().set_minimized(false);
|
||||
(*ui_->stream)->show();
|
||||
}
|
||||
need_to_create_stream_window_ = false;
|
||||
}
|
||||
#if _WIN32
|
||||
@@ -3802,7 +3803,8 @@ void GuiApplication::SyncStreamWindow() {
|
||||
if (ui_->video_presenter && ui_->video_presenter->EnsureAttached()) {
|
||||
video_frame_dirty_.store(true, std::memory_order_release);
|
||||
}
|
||||
if (ui_->stream_initial_position_attempts > 0) {
|
||||
if (has_sessions && ui_->stream_initial_position_attempts > 0 &&
|
||||
NativeWindowReady((*ui_->stream)->window())) {
|
||||
if (PositionWindowAtCenter(
|
||||
(*ui_->stream)->window(), ui_->main->window(),
|
||||
kStreamWindowLogicalWidth,
|
||||
@@ -3811,6 +3813,7 @@ void GuiApplication::SyncStreamWindow() {
|
||||
} else {
|
||||
ui_->stream_initial_position_attempts = 0;
|
||||
}
|
||||
if (ui_->stream_initial_position_attempts == 0) (*ui_->stream)->show();
|
||||
}
|
||||
if (!has_sessions) {
|
||||
SetStreamKeyboardFocus(false);
|
||||
@@ -4211,11 +4214,7 @@ void GuiApplication::SetServerPanelExpanded(bool expanded) {
|
||||
server_window_collapsed_ = !expanded;
|
||||
(*ui_->server_bar)->set_expanded(expanded);
|
||||
if (expanded) {
|
||||
PositionServerWindows();
|
||||
(*ui_->server)->show();
|
||||
#if _WIN32
|
||||
ConfigureWindowsServerWindow((*ui_->server)->window());
|
||||
#endif
|
||||
// SyncServerWindow shows the panel after native creation and placement.
|
||||
PositionServerWindows();
|
||||
} else {
|
||||
(*ui_->server)->set_confirming_disconnect(false);
|
||||
@@ -4447,10 +4446,6 @@ void GuiApplication::SyncServerWindow() {
|
||||
BindServerCallbacks();
|
||||
ui_localization::ApplyServerStrings(*ui_->server, localization_language_index_);
|
||||
ui_localization::ApplyServerStrings(*ui_->server_bar, localization_language_index_);
|
||||
(*ui_->server_bar)->show();
|
||||
#if _WIN32
|
||||
ui_->server_native_window_attempts = 30;
|
||||
#endif
|
||||
server_window_created_ = true;
|
||||
server_window_inited_ = true;
|
||||
}
|
||||
@@ -4470,13 +4465,9 @@ void GuiApplication::SyncServerWindow() {
|
||||
ui_->server_drag.active = false;
|
||||
server_window_created_ = false;
|
||||
server_window_inited_ = false;
|
||||
#if _WIN32
|
||||
ui_->server_native_window_attempts = 0;
|
||||
#endif
|
||||
}
|
||||
if (!ui_->server) return;
|
||||
if (ui_->server_suspended_for_dialog) {
|
||||
(*ui_->server_bar)->show();
|
||||
ui_->server_suspended_for_dialog = false;
|
||||
}
|
||||
|
||||
@@ -4517,16 +4508,17 @@ void GuiApplication::SyncServerWindow() {
|
||||
(*ui_->server_bar)->set_controller_count(static_cast<int>(ui_->controller_ids.size()));
|
||||
ui_localization::ApplyServerStrings(*ui_->server, localization_language_index_);
|
||||
ui_localization::ApplyServerStrings(*ui_->server_bar, localization_language_index_);
|
||||
#if _WIN32
|
||||
if (ui_->server_native_window_attempts > 0) {
|
||||
if (ConfigureWindowsServerWindow((*ui_->server_bar)->window()))
|
||||
ui_->server_native_window_attempts = 0;
|
||||
else
|
||||
--ui_->server_native_window_attempts;
|
||||
}
|
||||
#endif
|
||||
(*ui_->server)->set_sending_file(transfers_.global_state().file_sending_.load());
|
||||
PositionServerWindows();
|
||||
const auto show_prepared = [](slint::Window& window) {
|
||||
if (window.is_visible() || !NativeWindowReady(window)) return;
|
||||
#if _WIN32
|
||||
if (!ConfigureWindowsServerWindow(window)) return;
|
||||
#endif
|
||||
window.show();
|
||||
};
|
||||
show_prepared((*ui_->server_bar)->window());
|
||||
if (!server_window_collapsed_) show_prepared((*ui_->server)->window());
|
||||
}
|
||||
|
||||
void GuiApplication::CancelFileTransfer(uint32_t file_id) {
|
||||
@@ -4759,13 +4751,8 @@ void GuiApplication::SyncFileTransferNotifications() {
|
||||
#endif
|
||||
}
|
||||
if (!window.is_visible()) {
|
||||
#if defined(__APPLE__)
|
||||
// Winit creates native windows on the next event-loop turn. Wait until
|
||||
// it exists so show() and focus restoration happen synchronously.
|
||||
if (!window.appkit_view()) continue;
|
||||
#elif _WIN32
|
||||
if (!window.win32_hwnd()) continue;
|
||||
#endif
|
||||
// Wait until native placement and focus restoration can be synchronous.
|
||||
if (!NativeWindowReady(window)) continue;
|
||||
#if _WIN32
|
||||
// Tool windows stay out of Alt-Tab and must not interrupt remote input.
|
||||
const auto configure = [&] {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <slint.h>
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
// Winit creates hidden native windows on a later event-loop turn. Wait for
|
||||
// that window before applying geometry that depends on its frame or DPI.
|
||||
inline bool NativeWindowReady([[maybe_unused]] const slint::Window& window) {
|
||||
#if defined(_WIN32)
|
||||
return window.win32_hwnd() != nullptr;
|
||||
#elif defined(__APPLE__)
|
||||
return window.appkit_view() != nullptr;
|
||||
#else
|
||||
// Other backends accept initial geometry before show(); Wayland placement
|
||||
// is controlled by the compositor and must not wait for a mapped surface.
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
@@ -10,6 +10,7 @@ extern unsigned int fa_solid_900_ttf_len;
|
||||
#include "ui/model_update.h"
|
||||
#include "localization.h"
|
||||
#include "application/window_geometry.h"
|
||||
#include "application/window_presentation.h"
|
||||
#if defined(__APPLE__)
|
||||
#include "platform/window_drag.h"
|
||||
#elif defined(_WIN32)
|
||||
@@ -54,6 +55,9 @@ FileBrowserWindow::FileBrowserWindow(FileTransferManager& transfers, std::string
|
||||
transfers_.browser().TakeEvents(remote_);
|
||||
window_->window().window_handle().register_font_from_data(
|
||||
fa_solid_900_ttf, fa_solid_900_ttf_len);
|
||||
// Resolve the native frame at its final size while it is still hidden.
|
||||
// Otherwise show() applies the preferred size after we have centered it.
|
||||
window_->window().set_size(slint::LogicalSize({900, 620}));
|
||||
window_->set_chinese(chinese_);
|
||||
UpdateActionLabels(language);
|
||||
SetPeerName("");
|
||||
@@ -177,6 +181,7 @@ FileBrowserWindow::FileBrowserWindow(FileTransferManager& transfers, std::string
|
||||
next_sync_ = {};
|
||||
});
|
||||
window_->window().on_close_requested([this] {
|
||||
show_pending_ = false;
|
||||
CancelDrag();
|
||||
window_->hide(); return slint::CloseRequestResponse::KeepWindowShown;
|
||||
});
|
||||
@@ -246,19 +251,23 @@ bool FileBrowserWindow::CenterOnScreen() {
|
||||
}
|
||||
void FileBrowserWindow::Show() {
|
||||
if (!Tick()) return;
|
||||
window_->window().set_minimized(false);
|
||||
window_->show();
|
||||
#if defined(__APPLE__)
|
||||
// Winit may create the NSWindow after show() returns. Retry on UI ticks,
|
||||
// including when a hidden file window is opened again.
|
||||
native_titlebar_attempts_ =
|
||||
ConfigureWhiteWindowChrome(window_->window().appkit_view()) ? 0 : 30;
|
||||
#endif
|
||||
// Always recenter a reopened window. Only retry until the native frame is
|
||||
// ready; subsequent UI ticks must leave user-driven window movement alone.
|
||||
center_attempts_ = CenterOnScreen() ? 0 : 30;
|
||||
if (Visible()) {
|
||||
window_->window().set_minimized(false);
|
||||
} else {
|
||||
show_pending_ = true;
|
||||
ShowWhenReady();
|
||||
}
|
||||
if (!panes_[1].busy && !panes_[1].error.empty()) Navigate(true, panes_[1].path);
|
||||
}
|
||||
void FileBrowserWindow::ShowWhenReady() {
|
||||
if (!show_pending_ || !NativeWindowReady(window_->window())) return;
|
||||
#if defined(__APPLE__)
|
||||
if (!ConfigureWhiteWindowChrome(window_->window().appkit_view())) return;
|
||||
#endif
|
||||
if (!CenterOnScreen()) return;
|
||||
show_pending_ = false;
|
||||
window_->show();
|
||||
}
|
||||
void FileBrowserWindow::OpenDirectory(bool remote, const std::string& path) {
|
||||
const auto& pane = panes_[remote];
|
||||
if (!SessionConnected() || !pane.loaded || pane.busy || !pane.error.empty() ||
|
||||
@@ -480,24 +489,14 @@ bool FileBrowserWindow::Tick() {
|
||||
// directory/progress refresh. All native window operations stay on this thread.
|
||||
if (!SessionConnected()) {
|
||||
disconnected_ = true;
|
||||
show_pending_ = false;
|
||||
window_->set_connected(false);
|
||||
CancelDrag();
|
||||
requests_.clear();
|
||||
window_->hide();
|
||||
return false;
|
||||
}
|
||||
#if defined(__APPLE__)
|
||||
if (native_titlebar_attempts_ > 0 && Visible()) {
|
||||
if (ConfigureWhiteWindowChrome(window_->window().appkit_view()))
|
||||
native_titlebar_attempts_ = 0;
|
||||
else
|
||||
--native_titlebar_attempts_;
|
||||
}
|
||||
#endif
|
||||
if (center_attempts_ > 0 && Visible()) {
|
||||
if (CenterOnScreen()) center_attempts_ = 0;
|
||||
else --center_attempts_;
|
||||
}
|
||||
ShowWhenReady();
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now < next_sync_) return true;
|
||||
next_sync_ = now + std::chrono::milliseconds(150);
|
||||
|
||||
@@ -61,6 +61,7 @@ class FileBrowserWindow {
|
||||
void Navigate(bool remote, const std::string& path, bool tree = false);
|
||||
void OpenDirectory(bool remote, const std::string& path);
|
||||
bool CenterOnScreen();
|
||||
void ShowWhenReady();
|
||||
void UpdatePane(bool remote);
|
||||
void SortEntries(Pane& pane);
|
||||
void UpdateTree(bool remote);
|
||||
@@ -82,10 +83,7 @@ class FileBrowserWindow {
|
||||
uint64_t connection_generation_;
|
||||
bool chinese_;
|
||||
bool disconnected_ = false;
|
||||
int center_attempts_ = 0;
|
||||
#if defined(__APPLE__)
|
||||
int native_titlebar_attempts_ = 0;
|
||||
#endif
|
||||
bool show_pending_ = false;
|
||||
slint::ComponentHandle<ui::FileBrowserWindow> window_ = ui::FileBrowserWindow::create();
|
||||
Pane panes_[2];
|
||||
std::shared_ptr<int> drag_owner_ = std::make_shared<int>(0);
|
||||
|
||||
Reference in New Issue
Block a user