mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-08-26 13:29:31 +08:00
[feat] refine tray and main window behavior
This commit is contained in:
@@ -34,6 +34,8 @@ int ConfigCenter::Load() {
|
|||||||
persist_config_migration |= ini_.Delete(section_, "screen_content");
|
persist_config_migration |= ini_.Delete(section_, "screen_content");
|
||||||
persist_config_migration |=
|
persist_config_migration |=
|
||||||
ini_.Delete(section_, "enable_desktop_quality_optimization");
|
ini_.Delete(section_, "enable_desktop_quality_optimization");
|
||||||
|
persist_config_migration |=
|
||||||
|
ini_.Delete(section_, "enable_minimize_to_tray");
|
||||||
|
|
||||||
const long language_value =
|
const long language_value =
|
||||||
ini_.GetLongValue(section_, "language", static_cast<long>(language_));
|
ini_.GetLongValue(section_, "language", static_cast<long>(language_));
|
||||||
@@ -109,8 +111,6 @@ int ConfigCenter::Load() {
|
|||||||
enable_autostart_ =
|
enable_autostart_ =
|
||||||
ini_.GetBoolValue(section_, "enable_autostart", enable_autostart_);
|
ini_.GetBoolValue(section_, "enable_autostart", enable_autostart_);
|
||||||
enable_daemon_ = ini_.GetBoolValue(section_, "enable_daemon", enable_daemon_);
|
enable_daemon_ = ini_.GetBoolValue(section_, "enable_daemon", enable_daemon_);
|
||||||
enable_minimize_to_tray_ = ini_.GetBoolValue(
|
|
||||||
section_, "enable_minimize_to_tray", enable_minimize_to_tray_);
|
|
||||||
portable_service_prompt_suppressed_ =
|
portable_service_prompt_suppressed_ =
|
||||||
ini_.GetBoolValue(section_, "portable_service_prompt_suppressed",
|
ini_.GetBoolValue(section_, "portable_service_prompt_suppressed",
|
||||||
portable_service_prompt_suppressed_);
|
portable_service_prompt_suppressed_);
|
||||||
@@ -157,8 +157,6 @@ int ConfigCenter::Save() {
|
|||||||
|
|
||||||
ini_.SetBoolValue(section_, "enable_autostart", enable_autostart_);
|
ini_.SetBoolValue(section_, "enable_autostart", enable_autostart_);
|
||||||
ini_.SetBoolValue(section_, "enable_daemon", enable_daemon_);
|
ini_.SetBoolValue(section_, "enable_daemon", enable_daemon_);
|
||||||
ini_.SetBoolValue(section_, "enable_minimize_to_tray",
|
|
||||||
enable_minimize_to_tray_);
|
|
||||||
ini_.SetBoolValue(section_, "portable_service_prompt_suppressed",
|
ini_.SetBoolValue(section_, "portable_service_prompt_suppressed",
|
||||||
portable_service_prompt_suppressed_);
|
portable_service_prompt_suppressed_);
|
||||||
|
|
||||||
@@ -338,17 +336,6 @@ int ConfigCenter::SetSelfHosted(bool enable_self_hosted) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ConfigCenter::SetMinimizeToTray(bool enable_minimize_to_tray) {
|
|
||||||
enable_minimize_to_tray_ = enable_minimize_to_tray;
|
|
||||||
ini_.SetBoolValue(section_, "enable_minimize_to_tray",
|
|
||||||
enable_minimize_to_tray_);
|
|
||||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
|
||||||
if (rc < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ConfigCenter::SetAutostart(bool enable_autostart) {
|
int ConfigCenter::SetAutostart(bool enable_autostart) {
|
||||||
enable_autostart_ = enable_autostart;
|
enable_autostart_ = enable_autostart;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
@@ -448,8 +435,6 @@ int ConfigCenter::GetDefaultCoturnServerPort() const {
|
|||||||
|
|
||||||
bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; }
|
bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; }
|
||||||
|
|
||||||
bool ConfigCenter::IsMinimizeToTray() const { return enable_minimize_to_tray_; }
|
|
||||||
|
|
||||||
bool ConfigCenter::IsEnableAutostart() const { return enable_autostart_; }
|
bool ConfigCenter::IsEnableAutostart() const { return enable_autostart_; }
|
||||||
|
|
||||||
bool ConfigCenter::IsEnableDaemon() const { return enable_daemon_; }
|
bool ConfigCenter::IsEnableDaemon() const { return enable_daemon_; }
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ class ConfigCenter {
|
|||||||
int SetServerPort(int signal_server_port);
|
int SetServerPort(int signal_server_port);
|
||||||
int SetCoturnServerPort(int coturn_server_port);
|
int SetCoturnServerPort(int coturn_server_port);
|
||||||
int SetSelfHosted(bool enable_self_hosted);
|
int SetSelfHosted(bool enable_self_hosted);
|
||||||
int SetMinimizeToTray(bool enable_minimize_to_tray);
|
|
||||||
int SetAutostart(bool enable_autostart);
|
int SetAutostart(bool enable_autostart);
|
||||||
int SetDaemon(bool enable_daemon);
|
int SetDaemon(bool enable_daemon);
|
||||||
int SetPortableServicePromptSuppressed(bool suppressed);
|
int SetPortableServicePromptSuppressed(bool suppressed);
|
||||||
@@ -66,7 +65,6 @@ class ConfigCenter {
|
|||||||
int GetDefaultSignalServerPort() const;
|
int GetDefaultSignalServerPort() const;
|
||||||
int GetDefaultCoturnServerPort() const;
|
int GetDefaultCoturnServerPort() const;
|
||||||
bool IsSelfHosted() const;
|
bool IsSelfHosted() const;
|
||||||
bool IsMinimizeToTray() const;
|
|
||||||
bool IsEnableAutostart() const;
|
bool IsEnableAutostart() const;
|
||||||
bool IsEnableDaemon() const;
|
bool IsEnableDaemon() const;
|
||||||
bool IsPortableServicePromptSuppressed() const;
|
bool IsPortableServicePromptSuppressed() const;
|
||||||
@@ -94,7 +92,6 @@ class ConfigCenter {
|
|||||||
int coturn_server_port_ = 0;
|
int coturn_server_port_ = 0;
|
||||||
int coturn_server_port_default_ = 3478;
|
int coturn_server_port_default_ = 3478;
|
||||||
bool enable_self_hosted_ = false;
|
bool enable_self_hosted_ = false;
|
||||||
bool enable_minimize_to_tray_ = false;
|
|
||||||
bool enable_autostart_ = false;
|
bool enable_autostart_ = false;
|
||||||
bool enable_daemon_ = false;
|
bool enable_daemon_ = false;
|
||||||
bool portable_service_prompt_suppressed_ = false;
|
bool portable_service_prompt_suppressed_ = false;
|
||||||
|
|||||||
@@ -1163,22 +1163,24 @@ void GuiApplication::InitializeSystemTray() {
|
|||||||
};
|
};
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
ui_->tray = std::make_unique<WinTray>(
|
ui_->tray = std::make_unique<WinTray>(
|
||||||
std::move(show_window), std::move(hide_window), std::move(exit_app),
|
std::move(show_window), std::move(hide_window),
|
||||||
LoadSlintTrayIcon(), L"CrossDesk", localization_language_index_);
|
std::move(open_settings), std::move(exit_app), LoadSlintTrayIcon(),
|
||||||
|
L"CrossDesk", localization_language_index_);
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
ui_->tray = std::make_unique<MacTray>(
|
ui_->tray = std::make_unique<MacTray>(
|
||||||
std::move(show_window), std::move(hide_window), std::move(open_settings),
|
std::move(show_window), std::move(hide_window), std::move(open_settings),
|
||||||
std::move(exit_app), "CrossDesk", localization_language_index_);
|
std::move(exit_app), "CrossDesk", localization_language_index_);
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
ui_->tray = std::make_unique<LinuxTray>(
|
ui_->tray = std::make_unique<LinuxTray>(
|
||||||
std::move(show_window), std::move(hide_window), std::move(exit_app),
|
std::move(show_window), std::move(hide_window),
|
||||||
"CrossDesk", localization_language_index_);
|
std::move(open_settings), std::move(exit_app), "CrossDesk",
|
||||||
|
localization_language_index_);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GuiApplication::MinimizeMainWindowToTray() {
|
bool GuiApplication::MinimizeMainWindowToTray() {
|
||||||
if (!enable_minimize_to_tray_ || !ui_) {
|
if (!ui_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#if _WIN32 || defined(__APPLE__)
|
#if _WIN32 || defined(__APPLE__)
|
||||||
@@ -1208,7 +1210,6 @@ void GuiApplication::ResetSettingsUi() {
|
|||||||
ui_->main->set_self_hosted_enabled(enable_self_hosted_);
|
ui_->main->set_self_hosted_enabled(enable_self_hosted_);
|
||||||
ui_->main->set_autostart_enabled(enable_autostart_);
|
ui_->main->set_autostart_enabled(enable_autostart_);
|
||||||
ui_->main->set_daemon_enabled(enable_daemon_);
|
ui_->main->set_daemon_enabled(enable_daemon_);
|
||||||
ui_->main->set_minimize_to_tray_enabled(enable_minimize_to_tray_);
|
|
||||||
ui_->main->set_file_save_path(file_transfer_save_path_buf_);
|
ui_->main->set_file_save_path(file_transfer_save_path_buf_);
|
||||||
ui_->main->set_server_host(signal_server_ip_self_);
|
ui_->main->set_server_host(signal_server_ip_self_);
|
||||||
ui_->main->set_server_port(signal_server_port_self_);
|
ui_->main->set_server_port(signal_server_port_self_);
|
||||||
@@ -1218,12 +1219,10 @@ void GuiApplication::ResetSettingsUi() {
|
|||||||
void GuiApplication::BindMainCallbacks() {
|
void GuiApplication::BindMainCallbacks() {
|
||||||
auto& main = ui_->main;
|
auto& main = ui_->main;
|
||||||
main->window().on_close_requested([this] {
|
main->window().on_close_requested([this] {
|
||||||
if (MinimizeMainWindowToTray()) {
|
// Keep the application and all active sessions alive. The tray helper
|
||||||
// MinimizeMainWindowToTray() ensures the tray icon exists. Let Slint
|
// ensures that supported platforms still provide a way to restore the
|
||||||
// complete the close request by hiding the main window as well.
|
// main window; Slint hides it regardless of tray availability.
|
||||||
return slint::CloseRequestResponse::HideWindow;
|
MinimizeMainWindowToTray();
|
||||||
}
|
|
||||||
exit_ = true;
|
|
||||||
return slint::CloseRequestResponse::HideWindow;
|
return slint::CloseRequestResponse::HideWindow;
|
||||||
});
|
});
|
||||||
main->on_main_title_drag([this](int phase, float x, float y) {
|
main->on_main_title_drag([this](int phase, float x, float y) {
|
||||||
@@ -1240,11 +1239,7 @@ void GuiApplication::BindMainCallbacks() {
|
|||||||
if (!ui_) {
|
if (!ui_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (MinimizeMainWindowToTray()) {
|
MinimizeMainWindowToTray();
|
||||||
ui_->main->hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
exit_ = true;
|
|
||||||
ui_->main->hide();
|
ui_->main->hide();
|
||||||
});
|
});
|
||||||
main->on_copy_local_id([this] {
|
main->on_copy_local_id([this] {
|
||||||
@@ -2756,7 +2751,6 @@ void GuiApplication::SaveSettingsFromUi() {
|
|||||||
enable_self_hosted_ = main->get_self_hosted_enabled();
|
enable_self_hosted_ = main->get_self_hosted_enabled();
|
||||||
enable_autostart_ = main->get_autostart_enabled();
|
enable_autostart_ = main->get_autostart_enabled();
|
||||||
enable_daemon_ = main->get_daemon_enabled();
|
enable_daemon_ = main->get_daemon_enabled();
|
||||||
enable_minimize_to_tray_ = main->get_minimize_to_tray_enabled();
|
|
||||||
|
|
||||||
localization_language_ =
|
localization_language_ =
|
||||||
static_cast<ConfigCenter::LANGUAGE>(language_button_value_);
|
static_cast<ConfigCenter::LANGUAGE>(language_button_value_);
|
||||||
@@ -2775,7 +2769,6 @@ void GuiApplication::SaveSettingsFromUi() {
|
|||||||
config_center_->SetSelfHosted(enable_self_hosted_);
|
config_center_->SetSelfHosted(enable_self_hosted_);
|
||||||
config_center_->SetAutostart(enable_autostart_);
|
config_center_->SetAutostart(enable_autostart_);
|
||||||
config_center_->SetDaemon(enable_daemon_);
|
config_center_->SetDaemon(enable_daemon_);
|
||||||
config_center_->SetMinimizeToTray(enable_minimize_to_tray_);
|
|
||||||
|
|
||||||
const std::string path(main->get_file_save_path());
|
const std::string path(main->get_file_save_path());
|
||||||
std::memset(file_transfer_save_path_buf_, 0,
|
std::memset(file_transfer_save_path_buf_, 0,
|
||||||
@@ -2805,7 +2798,6 @@ void GuiApplication::SaveSettingsFromUi() {
|
|||||||
enable_self_hosted_last_ = enable_self_hosted_;
|
enable_self_hosted_last_ = enable_self_hosted_;
|
||||||
enable_autostart_last_ = enable_autostart_;
|
enable_autostart_last_ = enable_autostart_;
|
||||||
enable_daemon_last_ = enable_daemon_;
|
enable_daemon_last_ = enable_daemon_;
|
||||||
enable_minimize_to_tray_last_ = enable_minimize_to_tray_;
|
|
||||||
file_transfer_save_path_last_ = path;
|
file_transfer_save_path_last_ = path;
|
||||||
ui_->localized_language = -1;
|
ui_->localized_language = -1;
|
||||||
|
|
||||||
|
|||||||
@@ -134,8 +134,10 @@ void GuiApplication::ProcessSdlEvent(const SDL_Event &event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (main_window_ &&
|
if (main_window_ &&
|
||||||
event.window.windowID == SDL_GetWindowID(main_window_) &&
|
event.window.windowID == SDL_GetWindowID(main_window_)) {
|
||||||
MinimizeMainWindowToTray()) {
|
if (!MinimizeMainWindowToTray()) {
|
||||||
|
SDL_HideWindow(main_window_);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ int GuiApplication::DrawServerWindow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GuiApplication::MinimizeMainWindowToTray() {
|
bool GuiApplication::MinimizeMainWindowToTray() {
|
||||||
if (!enable_minimize_to_tray_ || !main_window_) {
|
if (!main_window_) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,8 +136,9 @@ CROSSDESK_LOCALIZATION_ALL(CROSSDESK_DECLARE_LOCALIZED_STRING)
|
|||||||
#undef CROSSDESK_DECLARE_LOCALIZED_STRING
|
#undef CROSSDESK_DECLARE_LOCALIZED_STRING
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
inline const wchar_t* GetExitProgramLabel(int language_index) {
|
inline const wchar_t* GetWindowsLocalizedLabel(
|
||||||
static std::vector<std::wstring> cache(GetSupportedLanguages().size());
|
int language_index, const char* key, const wchar_t* fallback,
|
||||||
|
std::vector<std::wstring>& cache) {
|
||||||
const int normalized_index = detail::ClampLanguageIndex(language_index);
|
const int normalized_index = detail::ClampLanguageIndex(language_index);
|
||||||
std::wstring& cached_text = cache[normalized_index];
|
std::wstring& cached_text = cache[normalized_index];
|
||||||
if (!cached_text.empty()) {
|
if (!cached_text.empty()) {
|
||||||
@@ -145,24 +146,46 @@ inline const wchar_t* GetExitProgramLabel(int language_index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string& utf8_text =
|
const std::string& utf8_text =
|
||||||
detail::GetTranslatedText("exit_program", normalized_index);
|
detail::GetTranslatedText(key, normalized_index);
|
||||||
if (utf8_text.empty()) {
|
if (utf8_text.empty()) {
|
||||||
cached_text = L"Exit";
|
cached_text = fallback;
|
||||||
return cached_text.c_str();
|
return cached_text.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
int wide_length =
|
int wide_length =
|
||||||
MultiByteToWideChar(CP_UTF8, 0, utf8_text.c_str(), -1, nullptr, 0);
|
MultiByteToWideChar(CP_UTF8, 0, utf8_text.c_str(), -1, nullptr, 0);
|
||||||
if (wide_length <= 0) {
|
if (wide_length <= 0) {
|
||||||
cached_text = L"Exit";
|
cached_text = fallback;
|
||||||
return cached_text.c_str();
|
return cached_text.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
cached_text.resize(static_cast<size_t>(wide_length - 1));
|
cached_text.resize(static_cast<size_t>(wide_length));
|
||||||
MultiByteToWideChar(CP_UTF8, 0, utf8_text.c_str(), -1, cached_text.data(),
|
if (MultiByteToWideChar(CP_UTF8, 0, utf8_text.c_str(), -1,
|
||||||
wide_length);
|
cached_text.data(), wide_length) <= 0) {
|
||||||
|
cached_text = fallback;
|
||||||
return cached_text.c_str();
|
return cached_text.c_str();
|
||||||
}
|
}
|
||||||
|
cached_text.pop_back();
|
||||||
|
return cached_text.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const wchar_t* GetShowMainWindowLabel(int language_index) {
|
||||||
|
static std::vector<std::wstring> cache(GetSupportedLanguages().size());
|
||||||
|
return GetWindowsLocalizedLabel(language_index, "show_main_window",
|
||||||
|
L"Show Main Window", cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const wchar_t* GetSettingsLabel(int language_index) {
|
||||||
|
static std::vector<std::wstring> cache(GetSupportedLanguages().size());
|
||||||
|
return GetWindowsLocalizedLabel(language_index, "settings", L"Settings",
|
||||||
|
cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const wchar_t* GetExitProgramLabel(int language_index) {
|
||||||
|
static std::vector<std::wstring> cache(GetSupportedLanguages().size());
|
||||||
|
return GetWindowsLocalizedLabel(language_index, "exit_program", L"Exit",
|
||||||
|
cache);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace localization
|
} // namespace localization
|
||||||
|
|||||||
@@ -215,8 +215,6 @@ struct TranslationRow {
|
|||||||
X(file_transfer_save_path, u8"文件接收保存路径:", \
|
X(file_transfer_save_path, u8"文件接收保存路径:", \
|
||||||
"File Transfer Save Path:", u8"Путь сохранения файлов:") \
|
"File Transfer Save Path:", u8"Путь сохранения файлов:") \
|
||||||
X(default_desktop, u8"桌面", "Desktop", u8"Рабочий стол") \
|
X(default_desktop, u8"桌面", "Desktop", u8"Рабочий стол") \
|
||||||
X(minimize_to_tray, u8"退出时最小化到系统托盘:", \
|
|
||||||
"Minimize on Exit:", u8"Сворачивать в трей при выходе:") \
|
|
||||||
X(resolution, u8"分辨率", "Res", u8"Разрешение") \
|
X(resolution, u8"分辨率", "Res", u8"Разрешение") \
|
||||||
X(connection_mode, u8"连接模式", "Mode", u8"Режим") \
|
X(connection_mode, u8"连接模式", "Mode", u8"Режим") \
|
||||||
X(connection_mode_direct, u8"直连", "Direct", u8"Прямой") \
|
X(connection_mode_direct, u8"直连", "Direct", u8"Прямой") \
|
||||||
@@ -233,6 +231,8 @@ struct TranslationRow {
|
|||||||
X(permission_required_message, u8"该应用需要授权以下权限:", \
|
X(permission_required_message, u8"该应用需要授权以下权限:", \
|
||||||
"The application requires the following permissions:", \
|
"The application requires the following permissions:", \
|
||||||
u8"Для работы приложения требуются следующие разрешения:") \
|
u8"Для работы приложения требуются следующие разрешения:") \
|
||||||
|
X(show_main_window, u8"显示主界面", "Show Main Window", \
|
||||||
|
u8"Показать главное окно") \
|
||||||
X(exit_program, u8"退出", "Exit", u8"Выход")
|
X(exit_program, u8"退出", "Exit", u8"Выход")
|
||||||
|
|
||||||
inline constexpr TranslationRow kTranslationRows[] = {
|
inline constexpr TranslationRow kTranslationRows[] = {
|
||||||
|
|||||||
@@ -310,7 +310,6 @@ int SettingsManager::Load() {
|
|||||||
owner_.enable_self_hosted_ = owner_.config_center_->IsSelfHosted();
|
owner_.enable_self_hosted_ = owner_.config_center_->IsSelfHosted();
|
||||||
owner_.enable_autostart_ = owner_.config_center_->IsEnableAutostart();
|
owner_.enable_autostart_ = owner_.config_center_->IsEnableAutostart();
|
||||||
owner_.enable_daemon_ = owner_.config_center_->IsEnableDaemon();
|
owner_.enable_daemon_ = owner_.config_center_->IsEnableDaemon();
|
||||||
owner_.enable_minimize_to_tray_ = owner_.config_center_->IsMinimizeToTray();
|
|
||||||
#if _WIN32 && CROSSDESK_PORTABLE
|
#if _WIN32 && CROSSDESK_PORTABLE
|
||||||
owner_.portable_service_prompt_suppressed_ =
|
owner_.portable_service_prompt_suppressed_ =
|
||||||
owner_.config_center_->IsPortableServicePromptSuppressed();
|
owner_.config_center_->IsPortableServicePromptSuppressed();
|
||||||
@@ -333,7 +332,6 @@ int SettingsManager::Load() {
|
|||||||
owner_.enable_srtp_last_ = owner_.enable_srtp_;
|
owner_.enable_srtp_last_ = owner_.enable_srtp_;
|
||||||
owner_.enable_self_hosted_last_ = owner_.enable_self_hosted_;
|
owner_.enable_self_hosted_last_ = owner_.enable_self_hosted_;
|
||||||
owner_.enable_autostart_last_ = owner_.enable_autostart_;
|
owner_.enable_autostart_last_ = owner_.enable_autostart_;
|
||||||
owner_.enable_minimize_to_tray_last_ = owner_.enable_minimize_to_tray_;
|
|
||||||
|
|
||||||
LOG_INFO("Load settings from cache file");
|
LOG_INFO("Load settings from cache file");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ namespace crossdesk {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr int kTrayIconSize = 24;
|
constexpr int kTrayIconSize = 24;
|
||||||
constexpr int kMenuHeight = 28;
|
constexpr int kMenuItemHeight = 28;
|
||||||
|
constexpr int kMenuItemCount = 3;
|
||||||
|
constexpr int kMenuHeight = kMenuItemHeight * kMenuItemCount;
|
||||||
constexpr int kMenuHorizontalPadding = 12;
|
constexpr int kMenuHorizontalPadding = 12;
|
||||||
constexpr int kDockTimeoutMs = 800;
|
constexpr int kDockTimeoutMs = 800;
|
||||||
constexpr int kMaxTrayEventsPerTick = 32;
|
constexpr int kMaxTrayEventsPerTick = 32;
|
||||||
@@ -50,7 +52,21 @@ bool IsAsciiPrintable(const std::string& text) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetMenuLabel(int language_index) {
|
std::string GetShowMainWindowMenuLabel(int language_index) {
|
||||||
|
const int normalized_index = localization::detail::ClampLanguageIndex(
|
||||||
|
language_index);
|
||||||
|
const std::string& label = localization::show_main_window[normalized_index];
|
||||||
|
return label.empty() ? "Show Main Window" : label;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetSettingsMenuLabel(int language_index) {
|
||||||
|
const int normalized_index = localization::detail::ClampLanguageIndex(
|
||||||
|
language_index);
|
||||||
|
const std::string& label = localization::settings[normalized_index];
|
||||||
|
return label.empty() ? "Settings" : label;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetExitMenuLabel(int language_index) {
|
||||||
const int normalized_index = localization::detail::ClampLanguageIndex(
|
const int normalized_index = localization::detail::ClampLanguageIndex(
|
||||||
language_index);
|
language_index);
|
||||||
const std::string& label = localization::exit_program[normalized_index];
|
const std::string& label = localization::exit_program[normalized_index];
|
||||||
@@ -187,20 +203,42 @@ struct LinuxTrayImpl {
|
|||||||
tooltip(std::move(tray_tooltip)),
|
tooltip(std::move(tray_tooltip)),
|
||||||
language_index(language_index_value),
|
language_index(language_index_value),
|
||||||
exit_event_type(tray_exit_event_type),
|
exit_event_type(tray_exit_event_type),
|
||||||
menu_label(GetMenuLabel(language_index_value)),
|
show_menu_label(GetShowMainWindowMenuLabel(language_index_value)),
|
||||||
menu_ascii_label(IsAsciiPrintable(menu_label) ? menu_label : "Exit") {}
|
show_menu_ascii_label(IsAsciiPrintable(show_menu_label)
|
||||||
|
? show_menu_label
|
||||||
|
: "Show Main Window"),
|
||||||
|
settings_menu_label(GetSettingsMenuLabel(language_index_value)),
|
||||||
|
settings_menu_ascii_label(IsAsciiPrintable(settings_menu_label)
|
||||||
|
? settings_menu_label
|
||||||
|
: "Settings"),
|
||||||
|
exit_menu_label(GetExitMenuLabel(language_index_value)),
|
||||||
|
exit_menu_ascii_label(IsAsciiPrintable(exit_menu_label)
|
||||||
|
? exit_menu_label
|
||||||
|
: "Exit") {}
|
||||||
|
|
||||||
explicit LinuxTrayImpl(std::function<void()> show_window_callback,
|
explicit LinuxTrayImpl(std::function<void()> show_window_callback,
|
||||||
std::function<void()> hide_window_callback,
|
std::function<void()> hide_window_callback,
|
||||||
|
std::function<void()> open_settings_callback,
|
||||||
std::function<void()> exit_callback,
|
std::function<void()> exit_callback,
|
||||||
std::string tray_tooltip, int language_index_value)
|
std::string tray_tooltip, int language_index_value)
|
||||||
: show_window(std::move(show_window_callback)),
|
: show_window(std::move(show_window_callback)),
|
||||||
hide_window(std::move(hide_window_callback)),
|
hide_window(std::move(hide_window_callback)),
|
||||||
|
open_settings(std::move(open_settings_callback)),
|
||||||
exit_app(std::move(exit_callback)),
|
exit_app(std::move(exit_callback)),
|
||||||
tooltip(std::move(tray_tooltip)),
|
tooltip(std::move(tray_tooltip)),
|
||||||
language_index(language_index_value),
|
language_index(language_index_value),
|
||||||
menu_label(GetMenuLabel(language_index_value)),
|
show_menu_label(GetShowMainWindowMenuLabel(language_index_value)),
|
||||||
menu_ascii_label(IsAsciiPrintable(menu_label) ? menu_label : "Exit") {}
|
show_menu_ascii_label(IsAsciiPrintable(show_menu_label)
|
||||||
|
? show_menu_label
|
||||||
|
: "Show Main Window"),
|
||||||
|
settings_menu_label(GetSettingsMenuLabel(language_index_value)),
|
||||||
|
settings_menu_ascii_label(IsAsciiPrintable(settings_menu_label)
|
||||||
|
? settings_menu_label
|
||||||
|
: "Settings"),
|
||||||
|
exit_menu_label(GetExitMenuLabel(language_index_value)),
|
||||||
|
exit_menu_ascii_label(IsAsciiPrintable(exit_menu_label)
|
||||||
|
? exit_menu_label
|
||||||
|
: "Exit") {}
|
||||||
|
|
||||||
~LinuxTrayImpl() { RemoveTrayIcon(); }
|
~LinuxTrayImpl() { RemoveTrayIcon(); }
|
||||||
|
|
||||||
@@ -540,6 +578,20 @@ struct LinuxTrayImpl {
|
|||||||
HandleButtonRelease(event.xbutton);
|
HandleButtonRelease(event.xbutton);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MotionNotify:
|
||||||
|
if (event.xmotion.window == menu_window) {
|
||||||
|
const int hovered_item =
|
||||||
|
event.xmotion.x >= 0 && event.xmotion.x < menu_width &&
|
||||||
|
event.xmotion.y >= 0 && event.xmotion.y < kMenuHeight
|
||||||
|
? event.xmotion.y / kMenuItemHeight
|
||||||
|
: -1;
|
||||||
|
if (hovered_item != hovered_menu_item) {
|
||||||
|
hovered_menu_item = hovered_item;
|
||||||
|
DrawMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case LeaveNotify:
|
case LeaveNotify:
|
||||||
if (event.xcrossing.window == menu_window) {
|
if (event.xcrossing.window == menu_window) {
|
||||||
HideMenu();
|
HideMenu();
|
||||||
@@ -556,9 +608,15 @@ struct LinuxTrayImpl {
|
|||||||
const bool inside_menu =
|
const bool inside_menu =
|
||||||
event.x >= 0 && event.y >= 0 && event.x < menu_width &&
|
event.x >= 0 && event.y >= 0 && event.x < menu_width &&
|
||||||
event.y < kMenuHeight;
|
event.y < kMenuHeight;
|
||||||
const bool activate_exit = inside_menu && event.button == Button1;
|
const int selected_item = inside_menu && event.button == Button1
|
||||||
|
? event.y / kMenuItemHeight
|
||||||
|
: -1;
|
||||||
HideMenu();
|
HideMenu();
|
||||||
if (activate_exit) {
|
if (selected_item == 0) {
|
||||||
|
ShowWindow();
|
||||||
|
} else if (selected_item == 1) {
|
||||||
|
OpenSettings();
|
||||||
|
} else if (selected_item == 2) {
|
||||||
RequestExit();
|
RequestExit();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -571,9 +629,7 @@ struct LinuxTrayImpl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.button == Button1) {
|
if (event.button == Button1 || event.button == Button3) {
|
||||||
ShowWindow();
|
|
||||||
} else if (event.button == Button3) {
|
|
||||||
ShowMenu(event.x_root, event.y_root);
|
ShowMenu(event.x_root, event.y_root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -824,29 +880,30 @@ struct LinuxTrayImpl {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MenuTextWidth() const {
|
int MenuTextWidth(const std::string& label,
|
||||||
|
const std::string& ascii_label) const {
|
||||||
if (menu_font) {
|
if (menu_font) {
|
||||||
XGlyphInfo extents{};
|
XGlyphInfo extents{};
|
||||||
XftTextExtentsUtf8(display, menu_font,
|
XftTextExtentsUtf8(display, menu_font,
|
||||||
reinterpret_cast<const FcChar8*>(menu_label.c_str()),
|
reinterpret_cast<const FcChar8*>(label.c_str()),
|
||||||
static_cast<int>(menu_label.size()), &extents);
|
static_cast<int>(label.size()), &extents);
|
||||||
return extents.xOff;
|
return extents.xOff;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (font_set) {
|
if (font_set) {
|
||||||
XRectangle ink{};
|
XRectangle ink{};
|
||||||
XRectangle logical{};
|
XRectangle logical{};
|
||||||
Xutf8TextExtents(font_set, menu_label.c_str(),
|
Xutf8TextExtents(font_set, label.c_str(), static_cast<int>(label.size()),
|
||||||
static_cast<int>(menu_label.size()), &ink, &logical);
|
&ink, &logical);
|
||||||
return logical.width;
|
return logical.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fallback_font) {
|
if (fallback_font) {
|
||||||
return XTextWidth(fallback_font, menu_ascii_label.c_str(),
|
return XTextWidth(fallback_font, ascii_label.c_str(),
|
||||||
static_cast<int>(menu_ascii_label.size()));
|
static_cast<int>(ascii_label.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<int>(menu_ascii_label.size()) * 8;
|
return static_cast<int>(ascii_label.size()) * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShowMenu(int root_x, int root_y) {
|
void ShowMenu(int root_x, int root_y) {
|
||||||
@@ -856,7 +913,15 @@ struct LinuxTrayImpl {
|
|||||||
|
|
||||||
HideMenu();
|
HideMenu();
|
||||||
|
|
||||||
menu_width = std::max(72, MenuTextWidth() + kMenuHorizontalPadding * 2);
|
menu_width =
|
||||||
|
std::max({72,
|
||||||
|
MenuTextWidth(show_menu_label, show_menu_ascii_label) +
|
||||||
|
kMenuHorizontalPadding * 2,
|
||||||
|
MenuTextWidth(settings_menu_label,
|
||||||
|
settings_menu_ascii_label) +
|
||||||
|
kMenuHorizontalPadding * 2,
|
||||||
|
MenuTextWidth(exit_menu_label, exit_menu_ascii_label) +
|
||||||
|
kMenuHorizontalPadding * 2});
|
||||||
const int display_width = DisplayWidth(display, screen);
|
const int display_width = DisplayWidth(display, screen);
|
||||||
const int display_height = DisplayHeight(display, screen);
|
const int display_height = DisplayHeight(display, screen);
|
||||||
const int x = std::clamp(root_x, 0, std::max(0, display_width - menu_width));
|
const int x = std::clamp(root_x, 0, std::max(0, display_width - menu_width));
|
||||||
@@ -868,7 +933,7 @@ struct LinuxTrayImpl {
|
|||||||
attrs.background_pixel = white_pixel;
|
attrs.background_pixel = white_pixel;
|
||||||
attrs.border_pixel = black_pixel;
|
attrs.border_pixel = black_pixel;
|
||||||
attrs.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask |
|
attrs.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask |
|
||||||
LeaveWindowMask;
|
PointerMotionMask | LeaveWindowMask;
|
||||||
menu_window = XCreateWindow(
|
menu_window = XCreateWindow(
|
||||||
display, root_window, x, y, menu_width, kMenuHeight, 1, CopyFromParent,
|
display, root_window, x, y, menu_width, kMenuHeight, 1, CopyFromParent,
|
||||||
InputOutput, CopyFromParent,
|
InputOutput, CopyFromParent,
|
||||||
@@ -878,10 +943,11 @@ struct LinuxTrayImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
menu_visible = true;
|
menu_visible = true;
|
||||||
|
hovered_menu_item = -1;
|
||||||
XMapRaised(display, menu_window);
|
XMapRaised(display, menu_window);
|
||||||
XGrabPointer(display, menu_window, False,
|
XGrabPointer(display, menu_window, False,
|
||||||
ButtonPressMask | ButtonReleaseMask, GrabModeAsync,
|
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
|
||||||
GrabModeAsync, None, None, CurrentTime);
|
GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
|
||||||
DrawMenu();
|
DrawMenu();
|
||||||
XFlush(display);
|
XFlush(display);
|
||||||
}
|
}
|
||||||
@@ -889,6 +955,7 @@ struct LinuxTrayImpl {
|
|||||||
void HideMenu() {
|
void HideMenu() {
|
||||||
if (!display || !menu_window) {
|
if (!display || !menu_window) {
|
||||||
menu_visible = false;
|
menu_visible = false;
|
||||||
|
hovered_menu_item = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -896,6 +963,7 @@ struct LinuxTrayImpl {
|
|||||||
XDestroyWindow(display, menu_window);
|
XDestroyWindow(display, menu_window);
|
||||||
menu_window = 0;
|
menu_window = 0;
|
||||||
menu_visible = false;
|
menu_visible = false;
|
||||||
|
hovered_menu_item = -1;
|
||||||
XFlush(display);
|
XFlush(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -907,45 +975,66 @@ struct LinuxTrayImpl {
|
|||||||
GC gc = XCreateGC(display, menu_window, 0, nullptr);
|
GC gc = XCreateGC(display, menu_window, 0, nullptr);
|
||||||
XSetForeground(display, gc, white_pixel);
|
XSetForeground(display, gc, white_pixel);
|
||||||
XFillRectangle(display, menu_window, gc, 0, 0, menu_width, kMenuHeight);
|
XFillRectangle(display, menu_window, gc, 0, 0, menu_width, kMenuHeight);
|
||||||
|
if (hovered_menu_item >= 0 && hovered_menu_item < kMenuItemCount) {
|
||||||
XSetForeground(display, gc, hover_pixel);
|
XSetForeground(display, gc, hover_pixel);
|
||||||
XFillRectangle(display, menu_window, gc, 1, 1, menu_width - 2,
|
XFillRectangle(display, menu_window, gc, 1,
|
||||||
kMenuHeight - 2);
|
hovered_menu_item * kMenuItemHeight + 1, menu_width - 2,
|
||||||
|
kMenuItemHeight - 2);
|
||||||
|
}
|
||||||
|
XSetForeground(display, gc, hover_pixel);
|
||||||
|
XDrawLine(display, menu_window, gc, 1, kMenuItemHeight * 2,
|
||||||
|
menu_width - 2, kMenuItemHeight * 2);
|
||||||
XSetForeground(display, gc, black_pixel);
|
XSetForeground(display, gc, black_pixel);
|
||||||
|
|
||||||
int baseline = kMenuHeight / 2 + 5;
|
XftDraw* xft_draw = menu_font && menu_text_color_allocated
|
||||||
if (menu_font && menu_text_color_allocated) {
|
? XftDrawCreate(display, menu_window,
|
||||||
XftDraw* draw = XftDrawCreate(display, menu_window,
|
|
||||||
DefaultVisual(display, screen),
|
DefaultVisual(display, screen),
|
||||||
DefaultColormap(display, screen));
|
DefaultColormap(display, screen))
|
||||||
if (draw) {
|
: nullptr;
|
||||||
baseline = (kMenuHeight - (menu_font->ascent + menu_font->descent)) /
|
auto draw_label = [&](int item_index, const std::string& label,
|
||||||
|
const std::string& ascii_label) {
|
||||||
|
const int item_y = item_index * kMenuItemHeight;
|
||||||
|
int baseline = item_y + kMenuItemHeight / 2 + 5;
|
||||||
|
if (xft_draw) {
|
||||||
|
baseline = item_y +
|
||||||
|
(kMenuItemHeight -
|
||||||
|
(menu_font->ascent + menu_font->descent)) /
|
||||||
2 +
|
2 +
|
||||||
menu_font->ascent;
|
menu_font->ascent;
|
||||||
XftDrawStringUtf8(
|
XftDrawStringUtf8(
|
||||||
draw, &menu_text_color, menu_font, kMenuHorizontalPadding, baseline,
|
xft_draw, &menu_text_color, menu_font, kMenuHorizontalPadding,
|
||||||
reinterpret_cast<const FcChar8*>(menu_label.c_str()),
|
baseline, reinterpret_cast<const FcChar8*>(label.c_str()),
|
||||||
static_cast<int>(menu_label.size()));
|
static_cast<int>(label.size()));
|
||||||
XftDrawDestroy(draw);
|
|
||||||
}
|
|
||||||
} else if (font_set) {
|
} else if (font_set) {
|
||||||
XFontSetExtents* extents = XExtentsOfFontSet(font_set);
|
XFontSetExtents* extents = XExtentsOfFontSet(font_set);
|
||||||
if (extents) {
|
if (extents) {
|
||||||
baseline = (kMenuHeight - extents->max_logical_extent.height) / 2 -
|
baseline = item_y +
|
||||||
|
(kMenuItemHeight -
|
||||||
|
extents->max_logical_extent.height) /
|
||||||
|
2 -
|
||||||
extents->max_logical_extent.y;
|
extents->max_logical_extent.y;
|
||||||
}
|
}
|
||||||
Xutf8DrawString(display, menu_window, font_set, gc,
|
Xutf8DrawString(display, menu_window, font_set, gc,
|
||||||
kMenuHorizontalPadding, baseline, menu_label.c_str(),
|
kMenuHorizontalPadding, baseline, label.c_str(),
|
||||||
static_cast<int>(menu_label.size()));
|
static_cast<int>(label.size()));
|
||||||
} else {
|
} else {
|
||||||
if (fallback_font) {
|
if (fallback_font) {
|
||||||
XSetFont(display, gc, fallback_font->fid);
|
XSetFont(display, gc, fallback_font->fid);
|
||||||
baseline = (kMenuHeight + fallback_font->ascent -
|
baseline = item_y +
|
||||||
|
(kMenuItemHeight + fallback_font->ascent -
|
||||||
fallback_font->descent) /
|
fallback_font->descent) /
|
||||||
2;
|
2;
|
||||||
}
|
}
|
||||||
XDrawString(display, menu_window, gc, kMenuHorizontalPadding, baseline,
|
XDrawString(display, menu_window, gc, kMenuHorizontalPadding, baseline,
|
||||||
menu_ascii_label.c_str(),
|
ascii_label.c_str(),
|
||||||
static_cast<int>(menu_ascii_label.size()));
|
static_cast<int>(ascii_label.size()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
draw_label(0, show_menu_label, show_menu_ascii_label);
|
||||||
|
draw_label(1, settings_menu_label, settings_menu_ascii_label);
|
||||||
|
draw_label(2, exit_menu_label, exit_menu_ascii_label);
|
||||||
|
if (xft_draw) {
|
||||||
|
XftDrawDestroy(xft_draw);
|
||||||
}
|
}
|
||||||
|
|
||||||
XFreeGC(display, gc);
|
XFreeGC(display, gc);
|
||||||
@@ -962,6 +1051,14 @@ struct LinuxTrayImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenSettings() {
|
||||||
|
if (open_settings) {
|
||||||
|
open_settings();
|
||||||
|
} else {
|
||||||
|
ShowWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RequestExit() {
|
void RequestExit() {
|
||||||
if (exit_app) {
|
if (exit_app) {
|
||||||
exit_app();
|
exit_app();
|
||||||
@@ -978,12 +1075,17 @@ struct LinuxTrayImpl {
|
|||||||
::SDL_Window* app_window = nullptr;
|
::SDL_Window* app_window = nullptr;
|
||||||
std::function<void()> show_window;
|
std::function<void()> show_window;
|
||||||
std::function<void()> hide_window;
|
std::function<void()> hide_window;
|
||||||
|
std::function<void()> open_settings;
|
||||||
std::function<void()> exit_app;
|
std::function<void()> exit_app;
|
||||||
std::string tooltip;
|
std::string tooltip;
|
||||||
int language_index = 0;
|
int language_index = 0;
|
||||||
uint32_t exit_event_type = 0;
|
uint32_t exit_event_type = 0;
|
||||||
std::string menu_label;
|
std::string show_menu_label;
|
||||||
std::string menu_ascii_label;
|
std::string show_menu_ascii_label;
|
||||||
|
std::string settings_menu_label;
|
||||||
|
std::string settings_menu_ascii_label;
|
||||||
|
std::string exit_menu_label;
|
||||||
|
std::string exit_menu_ascii_label;
|
||||||
Display* display = nullptr;
|
Display* display = nullptr;
|
||||||
int screen = 0;
|
int screen = 0;
|
||||||
::Window root_window = 0;
|
::Window root_window = 0;
|
||||||
@@ -1014,6 +1116,7 @@ struct LinuxTrayImpl {
|
|||||||
int icon_width = 0;
|
int icon_width = 0;
|
||||||
int icon_height = 0;
|
int icon_height = 0;
|
||||||
int menu_width = 72;
|
int menu_width = 72;
|
||||||
|
int hovered_menu_item = -1;
|
||||||
bool docked = false;
|
bool docked = false;
|
||||||
bool embedded = false;
|
bool embedded = false;
|
||||||
bool menu_visible = false;
|
bool menu_visible = false;
|
||||||
@@ -1028,11 +1131,13 @@ LinuxTray::LinuxTray(::SDL_Window* app_window, const std::string& tooltip,
|
|||||||
|
|
||||||
LinuxTray::LinuxTray(std::function<void()> show_window,
|
LinuxTray::LinuxTray(std::function<void()> show_window,
|
||||||
std::function<void()> hide_window,
|
std::function<void()> hide_window,
|
||||||
|
std::function<void()> open_settings,
|
||||||
std::function<void()> exit_app,
|
std::function<void()> exit_app,
|
||||||
const std::string& tooltip, int language_index)
|
const std::string& tooltip, int language_index)
|
||||||
: impl_(std::make_unique<LinuxTrayImpl>(
|
: impl_(std::make_unique<LinuxTrayImpl>(
|
||||||
std::move(show_window), std::move(hide_window), std::move(exit_app),
|
std::move(show_window), std::move(hide_window),
|
||||||
tooltip, language_index)) {}
|
std::move(open_settings), std::move(exit_app), tooltip,
|
||||||
|
language_index)) {}
|
||||||
|
|
||||||
LinuxTray::~LinuxTray() = default;
|
LinuxTray::~LinuxTray() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ class LinuxTray {
|
|||||||
LinuxTray(::SDL_Window* app_window, const std::string& tooltip,
|
LinuxTray(::SDL_Window* app_window, const std::string& tooltip,
|
||||||
int language_index, uint32_t exit_event_type);
|
int language_index, uint32_t exit_event_type);
|
||||||
LinuxTray(std::function<void()> show_window,
|
LinuxTray(std::function<void()> show_window,
|
||||||
std::function<void()> hide_window, std::function<void()> exit_app,
|
std::function<void()> hide_window,
|
||||||
|
std::function<void()> open_settings,
|
||||||
|
std::function<void()> exit_app,
|
||||||
const std::string& tooltip, int language_index);
|
const std::string& tooltip, int language_index);
|
||||||
~LinuxTray();
|
~LinuxTray();
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
@interface CrossDeskMacTrayTarget : NSObject
|
@interface CrossDeskMacTrayTarget : NSObject
|
||||||
- (instancetype)initWithOwner:(crossdesk::MacTrayImpl *)owner;
|
- (instancetype)initWithOwner:(crossdesk::MacTrayImpl *)owner;
|
||||||
- (void)statusItemClicked:(id)sender;
|
- (void)statusItemClicked:(id)sender;
|
||||||
|
- (void)showMainWindow:(id)sender;
|
||||||
- (void)openSettings:(id)sender;
|
- (void)openSettings:(id)sender;
|
||||||
- (void)exitApplication:(id)sender;
|
- (void)exitApplication:(id)sender;
|
||||||
@end
|
@end
|
||||||
@@ -84,6 +85,19 @@ struct MacTrayImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"CrossDesk"];
|
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"CrossDesk"];
|
||||||
|
[menu setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameAqua]];
|
||||||
|
|
||||||
|
NSString *show_main_window_title =
|
||||||
|
NSStringFromUtf8(localization::show_main_window
|
||||||
|
[localization::detail::ClampLanguageIndex(
|
||||||
|
language_index)]);
|
||||||
|
NSMenuItem *show_main_window_item =
|
||||||
|
[[NSMenuItem alloc] initWithTitle:show_main_window_title
|
||||||
|
action:@selector(showMainWindow:)
|
||||||
|
keyEquivalent:@""];
|
||||||
|
[show_main_window_item setTarget:target];
|
||||||
|
[menu addItem:show_main_window_item];
|
||||||
|
|
||||||
NSString *settings_title =
|
NSString *settings_title =
|
||||||
NSStringFromUtf8(localization::settings
|
NSStringFromUtf8(localization::settings
|
||||||
[localization::detail::ClampLanguageIndex(
|
[localization::detail::ClampLanguageIndex(
|
||||||
@@ -287,14 +301,7 @@ void MacTray::RemoveTrayIcon() { impl_->RemoveTrayIcon(); }
|
|||||||
if (!owner_) {
|
if (!owner_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSEvent *event = [NSApp currentEvent];
|
|
||||||
if (event && [event type] == NSEventTypeRightMouseUp) {
|
|
||||||
owner_->ShowMenu();
|
owner_->ShowMenu();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
owner_->ShowWindow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)exitApplication:(id)sender {
|
- (void)exitApplication:(id)sender {
|
||||||
@@ -304,6 +311,13 @@ void MacTray::RemoveTrayIcon() { impl_->RemoveTrayIcon(); }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)showMainWindow:(id)sender {
|
||||||
|
(void)sender;
|
||||||
|
if (owner_) {
|
||||||
|
owner_->ShowWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)openSettings:(id)sender {
|
- (void)openSettings:(id)sender {
|
||||||
(void)sender;
|
(void)sender;
|
||||||
if (owner_) {
|
if (owner_) {
|
||||||
|
|||||||
@@ -64,11 +64,13 @@ WinTray::WinTray(HWND app_hwnd, HICON icon, const std::wstring& tooltip,
|
|||||||
|
|
||||||
WinTray::WinTray(std::function<void()> show_window,
|
WinTray::WinTray(std::function<void()> show_window,
|
||||||
std::function<void()> hide_window,
|
std::function<void()> hide_window,
|
||||||
|
std::function<void()> open_settings,
|
||||||
std::function<void()> exit_app, HICON icon,
|
std::function<void()> exit_app, HICON icon,
|
||||||
const std::wstring& tooltip, int language_index)
|
const std::wstring& tooltip, int language_index)
|
||||||
: WinTray(nullptr, icon, tooltip, language_index) {
|
: WinTray(nullptr, icon, tooltip, language_index) {
|
||||||
show_window_ = std::move(show_window);
|
show_window_ = std::move(show_window);
|
||||||
hide_window_ = std::move(hide_window);
|
hide_window_ = std::move(hide_window);
|
||||||
|
open_settings_ = std::move(open_settings);
|
||||||
exit_app_ = std::move(exit_app);
|
exit_app_ = std::move(exit_app);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +99,14 @@ void WinTray::ShowApplicationWindow() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WinTray::OpenSettings() {
|
||||||
|
if (open_settings_) {
|
||||||
|
open_settings_();
|
||||||
|
} else {
|
||||||
|
ShowApplicationWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool WinTray::HandleTrayMessage(MSG* msg) {
|
bool WinTray::HandleTrayMessage(MSG* msg) {
|
||||||
if (!msg || msg->message != WM_TRAY_CALLBACK) return false;
|
if (!msg || msg->message != WM_TRAY_CALLBACK) return false;
|
||||||
|
|
||||||
@@ -111,6 +121,20 @@ bool WinTray::HandleTrayMessage(MSG* msg) {
|
|||||||
POINT pt;
|
POINT pt;
|
||||||
GetCursorPos(&pt);
|
GetCursorPos(&pt);
|
||||||
HMENU menu = CreatePopupMenu();
|
HMENU menu = CreatePopupMenu();
|
||||||
|
HBRUSH menu_background = CreateSolidBrush(RGB(255, 255, 255));
|
||||||
|
if (menu_background) {
|
||||||
|
MENUINFO menu_info{};
|
||||||
|
menu_info.cbSize = sizeof(menu_info);
|
||||||
|
menu_info.fMask = MIM_BACKGROUND | MIM_APPLYTOSUBMENUS;
|
||||||
|
menu_info.hbrBack = menu_background;
|
||||||
|
SetMenuInfo(menu, &menu_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendMenuW(menu, MF_STRING, 1002,
|
||||||
|
localization::GetShowMainWindowLabel(language_index_));
|
||||||
|
AppendMenuW(menu, MF_STRING, 1003,
|
||||||
|
localization::GetSettingsLabel(language_index_));
|
||||||
|
AppendMenuW(menu, MF_SEPARATOR, 0, nullptr);
|
||||||
AppendMenuW(menu, MF_STRING, 1001,
|
AppendMenuW(menu, MF_STRING, 1001,
|
||||||
localization::GetExitProgramLabel(language_index_));
|
localization::GetExitProgramLabel(language_index_));
|
||||||
|
|
||||||
@@ -119,6 +143,9 @@ bool WinTray::HandleTrayMessage(MSG* msg) {
|
|||||||
TrackPopupMenu(menu, TPM_RETURNCMD | TPM_NONOTIFY | TPM_LEFTALIGN,
|
TrackPopupMenu(menu, TPM_RETURNCMD | TPM_NONOTIFY | TPM_LEFTALIGN,
|
||||||
pt.x, pt.y, 0, hwnd_message_only_, nullptr);
|
pt.x, pt.y, 0, hwnd_message_only_, nullptr);
|
||||||
DestroyMenu(menu);
|
DestroyMenu(menu);
|
||||||
|
if (menu_background) {
|
||||||
|
DeleteObject(menu_background);
|
||||||
|
}
|
||||||
|
|
||||||
// handle menu command
|
// handle menu command
|
||||||
if (cmd == 1001) {
|
if (cmd == 1001) {
|
||||||
@@ -131,6 +158,8 @@ bool WinTray::HandleTrayMessage(MSG* msg) {
|
|||||||
}
|
}
|
||||||
} else if (cmd == 1002) {
|
} else if (cmd == 1002) {
|
||||||
ShowApplicationWindow();
|
ShowApplicationWindow();
|
||||||
|
} else if (cmd == 1003) {
|
||||||
|
OpenSettings();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ class WinTray {
|
|||||||
WinTray(HWND app_hwnd, HICON icon, const std::wstring& tooltip,
|
WinTray(HWND app_hwnd, HICON icon, const std::wstring& tooltip,
|
||||||
int language_index);
|
int language_index);
|
||||||
WinTray(std::function<void()> show_window,
|
WinTray(std::function<void()> show_window,
|
||||||
std::function<void()> hide_window, std::function<void()> exit_app,
|
std::function<void()> hide_window,
|
||||||
|
std::function<void()> open_settings,
|
||||||
|
std::function<void()> exit_app,
|
||||||
HICON icon, const std::wstring& tooltip, int language_index);
|
HICON icon, const std::wstring& tooltip, int language_index);
|
||||||
~WinTray();
|
~WinTray();
|
||||||
|
|
||||||
@@ -32,6 +34,7 @@ class WinTray {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void ShowApplicationWindow();
|
void ShowApplicationWindow();
|
||||||
|
void OpenSettings();
|
||||||
|
|
||||||
HWND app_hwnd_;
|
HWND app_hwnd_;
|
||||||
HWND hwnd_message_only_;
|
HWND hwnd_message_only_;
|
||||||
@@ -41,6 +44,7 @@ class WinTray {
|
|||||||
NOTIFYICONDATA nid_;
|
NOTIFYICONDATA nid_;
|
||||||
std::function<void()> show_window_;
|
std::function<void()> show_window_;
|
||||||
std::function<void()> hide_window_;
|
std::function<void()> hide_window_;
|
||||||
|
std::function<void()> open_settings_;
|
||||||
std::function<void()> exit_app_;
|
std::function<void()> exit_app_;
|
||||||
};
|
};
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
|
|||||||
@@ -140,8 +140,6 @@ struct UserSettingsState {
|
|||||||
bool enable_autostart_last_ = false;
|
bool enable_autostart_last_ = false;
|
||||||
bool enable_daemon_ = false;
|
bool enable_daemon_ = false;
|
||||||
bool enable_daemon_last_ = false;
|
bool enable_daemon_last_ = false;
|
||||||
bool enable_minimize_to_tray_ = false;
|
|
||||||
bool enable_minimize_to_tray_last_ = false;
|
|
||||||
char file_transfer_save_path_buf_[512] = "";
|
char file_transfer_save_path_buf_[512] = "";
|
||||||
std::string file_transfer_save_path_last_;
|
std::string file_transfer_save_path_last_;
|
||||||
char signal_server_ip_self_[256] = "";
|
char signal_server_ip_self_[256] = "";
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export global UiStrings {
|
|||||||
in-out property <string> self-hosted: "Self-hosted server";
|
in-out property <string> self-hosted: "Self-hosted server";
|
||||||
in-out property <string> autostart: "Start with system";
|
in-out property <string> autostart: "Start with system";
|
||||||
in-out property <string> daemon: "Run in background";
|
in-out property <string> daemon: "Run in background";
|
||||||
in-out property <string> minimize-to-tray: "Minimize to tray";
|
|
||||||
in-out property <string> file-save-path: "File save path";
|
in-out property <string> file-save-path: "File save path";
|
||||||
in-out property <string> default-desktop: "Desktop";
|
in-out property <string> default-desktop: "Desktop";
|
||||||
in-out property <string> server-host: "Server host";
|
in-out property <string> server-host: "Server host";
|
||||||
@@ -242,7 +241,6 @@ export component MainWindow inherits Window {
|
|||||||
in-out property <bool> self-hosted-enabled: false;
|
in-out property <bool> self-hosted-enabled: false;
|
||||||
in-out property <bool> autostart-enabled: false;
|
in-out property <bool> autostart-enabled: false;
|
||||||
in-out property <bool> daemon-enabled: false;
|
in-out property <bool> daemon-enabled: false;
|
||||||
in-out property <bool> minimize-to-tray-enabled: false;
|
|
||||||
in-out property <string> file-save-path: "";
|
in-out property <string> file-save-path: "";
|
||||||
in-out property <string> server-host: "";
|
in-out property <string> server-host: "";
|
||||||
in-out property <string> server-port: "";
|
in-out property <string> server-port: "";
|
||||||
@@ -1488,11 +1486,6 @@ export component MainWindow inherits Window {
|
|||||||
Text { x: 8px; y: 3px; width: root.compact-language ? 145px : 212px; height: 24px; text: UiStrings.daemon; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; }
|
Text { x: 8px; y: 3px; width: root.compact-language ? 145px : 212px; height: 24px; text: UiStrings.daemon; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; }
|
||||||
ImGuiCheckBox { x: root.compact-language ? 157px : 224px; y: 3px; checked <=> root.daemon-enabled; enabled: !root.settings-session-active; }
|
ImGuiCheckBox { x: root.compact-language ? 157px : 224px; y: 3px; checked <=> root.daemon-enabled; enabled: !root.settings-session-active; }
|
||||||
}
|
}
|
||||||
Rectangle {
|
|
||||||
height: 30px;
|
|
||||||
Text { x: 8px; y: 3px; width: root.compact-language ? 145px : 212px; height: 24px; text: UiStrings.minimize-to-tray; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; }
|
|
||||||
ImGuiCheckBox { x: root.compact-language ? 157px : 224px; y: 3px; checked <=> root.minimize-to-tray-enabled; enabled: !root.settings-session-active; }
|
|
||||||
}
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
Text { x: 8px; y: 3px; width: root.compact-language ? 94px : 154px; height: 24px; text: UiStrings.file-save-path; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; }
|
Text { x: 8px; y: 3px; width: root.compact-language ? 94px : 154px; height: 24px; text: UiStrings.file-save-path; font-size: ImGuiFontStyle.body; overflow: elide; vertical-alignment: center; }
|
||||||
|
|||||||
@@ -57,8 +57,6 @@ inline int ApplyMainWindowStrings(
|
|||||||
Text(localization::self_hosted_server_config[language]));
|
Text(localization::self_hosted_server_config[language]));
|
||||||
strings.set_autostart(Text(localization::enable_autostart[language]));
|
strings.set_autostart(Text(localization::enable_autostart[language]));
|
||||||
strings.set_daemon(Text(localization::enable_daemon[language]));
|
strings.set_daemon(Text(localization::enable_daemon[language]));
|
||||||
strings.set_minimize_to_tray(
|
|
||||||
Text(localization::minimize_to_tray[language]));
|
|
||||||
strings.set_file_save_path(
|
strings.set_file_save_path(
|
||||||
Text(localization::file_transfer_save_path[language]));
|
Text(localization::file_transfer_save_path[language]));
|
||||||
strings.set_default_desktop(Text(localization::default_desktop[language]));
|
strings.set_default_desktop(Text(localization::default_desktop[language]));
|
||||||
|
|||||||
@@ -299,8 +299,11 @@ int GuiApplication::TitleBar(bool main_window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (close_button_clicked) {
|
if (close_button_clicked) {
|
||||||
const bool minimized_to_tray = main_window && MinimizeMainWindowToTray();
|
if (main_window) {
|
||||||
if (!minimized_to_tray) {
|
if (!MinimizeMainWindowToTray()) {
|
||||||
|
SDL_HideWindow(main_window_);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
event.type = SDL_EVENT_QUIT;
|
event.type = SDL_EVENT_QUIT;
|
||||||
SDL_PushEvent(&event);
|
SDL_PushEvent(&event);
|
||||||
|
|||||||
@@ -355,26 +355,6 @@ int GuiApplication::SettingWindow() {
|
|||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
{
|
|
||||||
settings_items_offset += settings_items_padding;
|
|
||||||
ImGui::SetCursorPosY(settings_items_offset);
|
|
||||||
ImGui::AlignTextToFramePadding();
|
|
||||||
ImGui::Text("%s",
|
|
||||||
localization::minimize_to_tray[localization_language_index_]
|
|
||||||
.c_str());
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
|
||||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
|
|
||||||
} else {
|
|
||||||
ImGui::SetCursorPosX(title_bar_button_width_ * 5.955f);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Checkbox("##enable_minimize_to_tray_",
|
|
||||||
&enable_minimize_to_tray_);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
settings_items_offset += settings_items_padding;
|
settings_items_offset += settings_items_padding;
|
||||||
ImGui::SetCursorPosY(settings_items_offset);
|
ImGui::SetCursorPosY(settings_items_offset);
|
||||||
@@ -618,13 +598,6 @@ int GuiApplication::SettingWindow() {
|
|||||||
}
|
}
|
||||||
enable_daemon_last_ = enable_daemon_;
|
enable_daemon_last_ = enable_daemon_;
|
||||||
|
|
||||||
if (enable_minimize_to_tray_) {
|
|
||||||
config_center_->SetMinimizeToTray(true);
|
|
||||||
} else {
|
|
||||||
config_center_->SetMinimizeToTray(false);
|
|
||||||
}
|
|
||||||
enable_minimize_to_tray_last_ = enable_minimize_to_tray_;
|
|
||||||
|
|
||||||
// File transfer save path
|
// File transfer save path
|
||||||
config_center_->SetFileTransferSavePath(file_transfer_save_path_buf_);
|
config_center_->SetFileTransferSavePath(file_transfer_save_path_buf_);
|
||||||
file_transfer_save_path_last_ = file_transfer_save_path_buf_;
|
file_transfer_save_path_last_ = file_transfer_save_path_buf_;
|
||||||
|
|||||||
Reference in New Issue
Block a user