diff --git a/src/gui/assets/localization/localization_data.h b/src/gui/assets/localization/localization_data.h index ed4da26..785df53 100644 --- a/src/gui/assets/localization/localization_data.h +++ b/src/gui/assets/localization/localization_data.h @@ -19,155 +19,161 @@ struct TranslationRow { }; // Single source of truth for all UI strings. -#define CROSSDESK_LOCALIZATION_ALL(X) \ - X(local_desktop, u8"本桌面", "Local Desktop", u8"Локальный рабочий стол") \ - X(local_id, u8"本机ID", "Local ID", u8"Локальный ID") \ - X(local_id_copied_to_clipboard, u8"已复制到剪贴板", "Copied to clipboard", \ - u8"Скопировано в буфер обмена") \ - X(password, u8"密码", "Password", u8"Пароль") \ - X(max_password_len, u8"最大6个字符", "Max 6 chars", u8"Макс. 6 символов") \ - X(remote_desktop, u8"远程桌面", "Remote Desktop", \ - u8"Удаленный рабочий стол") \ - X(remote_id, u8"对端ID", "Remote ID", u8"Удаленный ID") \ - X(connect, u8"连接", "Connect", u8"Подключиться") \ - X(recent_connections, u8"近期连接", "Recent Connections", \ - u8"Недавние подключения") \ - X(disconnect, u8"断开连接", "Disconnect", u8"Отключить") \ - X(fullscreen, u8"全屏", " Fullscreen", u8"Полный экран") \ - X(show_net_traffic_stats, u8"显示流量统计", "Show Net Traffic Stats", \ - u8"Показать статистику трафика") \ - X(hide_net_traffic_stats, u8"隐藏流量统计", "Hide Net Traffic Stats", \ - u8"Скрыть статистику трафика") \ - X(video, u8"视频", "Video", u8"Видео") \ - X(audio, u8"音频", "Audio", u8"Аудио") \ - X(data, u8"数据", "Data", u8"Данные") \ - X(total, u8"总计", "Total", u8"Итого") \ - X(in, u8"输入", "In", u8"Вход") \ - X(out, u8"输出", "Out", u8"Выход") \ - X(loss_rate, u8"丢包率", "Loss Rate", u8"Потери пакетов") \ - X(exit_fullscreen, u8"退出全屏", "Exit fullscreen", \ - u8"Выйти из полноэкранного режима") \ - X(control_mouse, u8"控制", "Control", u8"Управление") \ - X(release_mouse, u8"释放", "Release", u8"Освободить") \ - X(audio_capture, u8"声音", "Audio", u8"Звук") \ - X(mute, u8" 静音", " Mute", u8"Без звука") \ - X(send_shortcut, u8"发送组合键", "Send Shortcut", u8"Сочетания клавиш") \ - X(send_sas, u8"发送SAS", "Send SAS", u8"Отправить SAS") \ - X(lock_remote, u8"锁定远端", "Lock Remote", u8"Заблокировать") \ - X(remote_password_box_visible, u8"远端密码框已出现", \ - "Remote password box visible", u8"Окно ввода пароля видно") \ - X(remote_lock_screen_hint, u8"远端处于锁屏封面,可发送SAS", \ - "Remote lock screen visible, send SAS", \ - u8"Видна блокировка, отправьте SAS") \ - X(remote_secure_desktop_active, u8"远端已进入安全桌面", \ - "Remote secure desktop active", \ - u8"Активен защищенный рабочий стол") \ - X(remote_service_unavailable, u8"远端Windows服务不可用", \ - "Remote Windows service unavailable", \ - u8"Служба Windows на удаленной стороне недоступна") \ - X(remote_unlock_requires_secure_desktop, \ - u8"当前仍需要安全桌面专用采集/输入", \ - "Secure desktop capture/input is still required", \ - u8"По-прежнему нужен отдельный захват/ввод для защищенного рабочего стола") \ - X(settings, u8"设置", "Settings", u8"Настройки") \ - X(language, u8"语言:", "Language:", u8"Язык:") \ - X(video_quality, u8"视频质量:", "Video Quality:", u8"Качество видео:") \ - X(video_frame_rate, u8"画面采集帧率:", \ - "Video Capture Frame Rate:", u8"Частота захвата видео:") \ - X(video_quality_high, u8"高", "High", u8"Высокое") \ - X(video_quality_medium, u8"中", "Medium", u8"Среднее") \ - X(video_quality_low, u8"低", "Low", u8"Низкое") \ - X(video_encode_format, u8"视频编码格式:", \ - "Video Encode Format:", u8"Формат кодека видео:") \ - X(av1, u8"AV1", "AV1", "AV1") \ - X(h264, u8"H.264", "H.264", "H.264") \ - X(enable_hardware_video_codec, u8"启用硬件编解码器:", \ - "Enable Hardware Video Codec:", u8"Использовать аппаратный кодек:") \ - X(enable_turn, u8"启用中继服务:", \ - "Enable TURN Service:", u8"Включить TURN-сервис:") \ - X(enable_srtp, u8"启用SRTP:", "Enable SRTP:", u8"Включить SRTP:") \ - X(self_hosted_server_config, u8"自托管配置", "Self-Hosted Config", \ - u8"Конфигурация self-hosted") \ - X(self_hosted_server_settings, u8"自托管设置", "Self-Hosted Settings", \ - u8"Настройки self-hosted") \ - X(self_hosted_server_address, u8"服务器地址:", \ - "Server Address:", u8"Адрес сервера:") \ - X(self_hosted_server_port, u8"信令服务端口:", \ - "Signal Service Port:", u8"Порт сигнального сервиса:") \ - X(self_hosted_server_coturn_server_port, u8"中继服务端口:", \ - "Relay Service Port:", u8"Порт реле-сервиса:") \ - X(ok, u8"确认", "OK", u8"ОК") \ - X(cancel, u8"取消", "Cancel", u8"Отмена") \ - X(new_password, u8"请输入六位密码:", \ - "Please input a six-char password:", u8"Введите шестизначный пароль:") \ - X(input_password, u8"请输入密码:", \ - "Please input password:", u8"Введите пароль:") \ - X(validate_password, u8"验证密码中...", "Validate password ...", \ - u8"Проверка пароля...") \ - X(reinput_password, u8"请重新输入密码", "Please input password again", \ - u8"Повторно введите пароль") \ - X(remember_password, u8"记住密码", "Remember password", \ - u8"Запомнить пароль") \ - X(signal_connected, u8"已连接服务器", "Connected", u8"Подключено к серверу") \ - X(signal_disconnected, u8"未连接服务器", "Disconnected", \ - u8"Нет подключения к серверу") \ - X(p2p_connected, u8"对等连接已建立", "P2P Connected", u8"P2P подключено") \ - X(p2p_disconnected, u8"对等连接已断开", "P2P Disconnected", \ - u8"P2P отключено") \ - X(p2p_connecting, u8"正在建立对等连接...", "P2P Connecting ...", \ - u8"Подключение P2P...") \ - X(receiving_screen, u8"画面接收中...", "Receiving screen...", \ - u8"Получение изображения...") \ - X(p2p_failed, u8"对等连接失败", "P2P Failed", u8"Сбой P2P") \ - X(p2p_closed, u8"对等连接已关闭", "P2P closed", u8"P2P закрыто") \ - X(no_such_id, u8"无此ID", "No such ID", u8"ID не найден") \ - X(about, u8"关于", "About", u8"О программе") \ - X(notification, u8"通知", "Notification", u8"Уведомление") \ - X(new_version_available, u8"新版本可用", "New Version Available", \ - u8"Доступна новая версия") \ - X(version, u8"版本", "Version", u8"Версия") \ - X(release_date, u8"发布日期: ", "Release Date: ", u8"Дата релиза: ") \ - X(access_website, u8"访问官网: ", \ - "Access Website: ", u8"Официальный сайт: ") \ - X(update, u8"更新", "Update", u8"Обновить") \ - X(confirm_delete_connection, u8"确认删除此连接", \ - "Confirm to delete this connection", u8"Удалить это подключение?") \ - X(enable_autostart, u8"开机自启:", "Auto Start:", u8"Автозапуск:") \ - X(enable_daemon, u8"启用守护进程:", "Enable Daemon:", u8"Включить демон:") \ - X(takes_effect_after_restart, u8"重启后生效", "Takes effect after restart", \ - u8"Вступит в силу после перезапуска") \ - X(select_file, u8"选择文件", "Select File", u8"Выбрать файл") \ - X(file_transfer_progress, u8"文件传输进度", "File Transfer Progress", \ - u8"Прогресс передачи файлов") \ - X(queued, u8"队列中", "Queued", u8"В очереди") \ - X(sending, u8"正在传输", "Sending", u8"Передача") \ - X(completed, u8"已完成", "Completed", u8"Завершено") \ - X(failed, u8"失败", "Failed", u8"Ошибка") \ - X(controller, u8"控制端:", "Controller:", u8"Контроллер:") \ - X(file_transfer, u8"文件传输:", "File Transfer:", u8"Передача файлов:") \ - X(connection_status, u8"连接状态:", \ - "Connection Status:", u8"Состояние соединения:") \ - X(file_transfer_save_path, u8"文件接收保存路径:", \ - "File Transfer Save Path:", u8"Путь сохранения файлов:") \ - X(default_desktop, u8"桌面", "Desktop", u8"Рабочий стол") \ - X(minimize_to_tray, u8"退出时最小化到系统托盘:", \ - "Minimize on Exit:", u8"Сворачивать в трей при выходе:") \ - X(resolution, u8"分辨率", "Res", u8"Разрешение") \ - X(connection_mode, u8"连接模式", "Mode", u8"Режим") \ - X(connection_mode_direct, u8"直连", "Direct", u8"Прямой") \ - X(connection_mode_relay, u8"中继", "Relay", u8"Релейный") \ - X(online, u8"在线", "Online", u8"Онлайн") \ - X(offline, u8"离线", "Offline", u8"Офлайн") \ - X(device_offline, u8"设备离线", "Device Offline", u8"Устройство офлайн") \ - X(request_permissions, u8"权限请求", "Request Permissions", \ - u8"Запрос разрешений") \ - X(screen_recording_permission, u8"屏幕录制权限", \ - "Screen Recording Permission", u8"Разрешение на запись экрана") \ - X(accessibility_permission, u8"辅助功能权限", "Accessibility Permission", \ - u8"Разрешение специальных возможностей") \ - X(permission_required_message, u8"该应用需要授权以下权限:", \ - "The application requires the following permissions:", \ - u8"Для работы приложения требуются следующие разрешения:") \ +#define CROSSDESK_LOCALIZATION_ALL(X) \ + X(local_desktop, u8"本桌面", "Local Desktop", u8"Локальный рабочий стол") \ + X(local_id, u8"本机ID", "Local ID", u8"Локальный ID") \ + X(local_id_copied_to_clipboard, u8"已复制到剪贴板", "Copied to clipboard", \ + u8"Скопировано в буфер обмена") \ + X(password, u8"密码", "Password", u8"Пароль") \ + X(max_password_len, u8"最大6个字符", "Max 6 chars", u8"Макс. 6 символов") \ + X(remote_desktop, u8"远程桌面", "Remote Desktop", \ + u8"Удаленный рабочий стол") \ + X(remote_id, u8"对端ID", "Remote ID", u8"Удаленный ID") \ + X(connect, u8"连接", "Connect", u8"Подключиться") \ + X(recent_connections, u8"近期连接", "Recent Connections", \ + u8"Недавние подключения") \ + X(disconnect, u8"断开连接", "Disconnect", u8"Отключить") \ + X(select_display, u8"选择显示器", "Select Display", u8"Выбрать дисплей") \ + X(expand_control_bar, u8"展开控制栏", "Expand Control Bar", \ + u8"Развернуть панель управления") \ + X(collapse_control_bar, u8"收起控制栏", "Collapse Control Bar", \ + u8"Свернуть панель управления") \ + X(fullscreen, u8"全屏", " Fullscreen", u8"Полный экран") \ + X(show_net_traffic_stats, u8"显示网络状态", "Show Net Traffic Stats", \ + u8"Показать статистику трафика") \ + X(hide_net_traffic_stats, u8"隐藏网络状态", "Hide Net Traffic Stats", \ + u8"Скрыть статистику трафика") \ + X(video, u8"视频", "Video", u8"Видео") \ + X(audio, u8"音频", "Audio", u8"Аудио") \ + X(data, u8"数据", "Data", u8"Данные") \ + X(total, u8"总计", "Total", u8"Итого") \ + X(in, u8"输入", "In", u8"Вход") \ + X(out, u8"输出", "Out", u8"Выход") \ + X(loss_rate, u8"丢包率", "Loss Rate", u8"Потери пакетов") \ + X(exit_fullscreen, u8"退出全屏", "Exit fullscreen", \ + u8"Выйти из полноэкранного режима") \ + X(control_mouse, u8"控制鼠标", "Control Mouse", u8"Управление мышью") \ + X(release_mouse, u8"释放鼠标", "Release Mouse", u8"Освободить мышь") \ + X(audio_capture, u8"播放声音", "Audio Capture", u8"Воспроизведение звука") \ + X(mute, u8" 静音", " Mute", u8"Без звука") \ + X(send_shortcut, u8"发送组合键", "Send Shortcut", u8"Сочетания клавиш") \ + X(send_sas, u8"发送SAS", "Send SAS", u8"Отправить SAS") \ + X(lock_remote, u8"锁定远端", "Lock Remote", u8"Заблокировать") \ + X(remote_password_box_visible, u8"远端密码框已出现", \ + "Remote password box visible", u8"Окно ввода пароля видно") \ + X(remote_lock_screen_hint, u8"远端处于锁屏封面,可发送SAS", \ + "Remote lock screen visible, send SAS", \ + u8"Видна блокировка, отправьте SAS") \ + X(remote_secure_desktop_active, u8"远端已进入安全桌面", \ + "Remote secure desktop active", u8"Активен защищенный рабочий стол") \ + X(remote_service_unavailable, u8"远端Windows服务不可用", \ + "Remote Windows service unavailable", \ + u8"Служба Windows на удаленной стороне недоступна") \ + X(remote_unlock_requires_secure_desktop, \ + u8"当前仍需要安全桌面专用采集/输入", \ + "Secure desktop capture/input is still required", \ + u8"По-прежнему нужен отдельный захват/ввод для защищенного рабочего " \ + u8"стола") \ + X(settings, u8"设置", "Settings", u8"Настройки") \ + X(language, u8"语言:", "Language:", u8"Язык:") \ + X(video_quality, u8"视频质量:", "Video Quality:", u8"Качество видео:") \ + X(video_frame_rate, u8"画面采集帧率:", \ + "Video Capture Frame Rate:", u8"Частота захвата видео:") \ + X(video_quality_high, u8"高", "High", u8"Высокое") \ + X(video_quality_medium, u8"中", "Medium", u8"Среднее") \ + X(video_quality_low, u8"低", "Low", u8"Низкое") \ + X(video_encode_format, u8"视频编码格式:", \ + "Video Encode Format:", u8"Формат кодека видео:") \ + X(av1, u8"AV1", "AV1", "AV1") \ + X(h264, u8"H.264", "H.264", "H.264") \ + X(enable_hardware_video_codec, u8"启用硬件编解码器:", \ + "Enable Hardware Video Codec:", u8"Использовать аппаратный кодек:") \ + X(enable_turn, u8"启用中继服务:", \ + "Enable TURN Service:", u8"Включить TURN-сервис:") \ + X(enable_srtp, u8"启用SRTP:", "Enable SRTP:", u8"Включить SRTP:") \ + X(self_hosted_server_config, u8"自托管配置", "Self-Hosted Config", \ + u8"Конфигурация self-hosted") \ + X(self_hosted_server_settings, u8"自托管设置", "Self-Hosted Settings", \ + u8"Настройки self-hosted") \ + X(self_hosted_server_address, u8"服务器地址:", \ + "Server Address:", u8"Адрес сервера:") \ + X(self_hosted_server_port, u8"信令服务端口:", \ + "Signal Service Port:", u8"Порт сигнального сервиса:") \ + X(self_hosted_server_coturn_server_port, u8"中继服务端口:", \ + "Relay Service Port:", u8"Порт реле-сервиса:") \ + X(ok, u8"确认", "OK", u8"ОК") \ + X(cancel, u8"取消", "Cancel", u8"Отмена") \ + X(new_password, u8"请输入六位密码:", \ + "Please input a six-char password:", u8"Введите шестизначный пароль:") \ + X(input_password, u8"请输入密码:", \ + "Please input password:", u8"Введите пароль:") \ + X(validate_password, u8"验证密码中...", "Validate password ...", \ + u8"Проверка пароля...") \ + X(reinput_password, u8"请重新输入密码", "Please input password again", \ + u8"Повторно введите пароль") \ + X(remember_password, u8"记住密码", "Remember password", \ + u8"Запомнить пароль") \ + X(signal_connected, u8"已连接服务器", "Connected", u8"Подключено к серверу") \ + X(signal_disconnected, u8"未连接服务器", "Disconnected", \ + u8"Нет подключения к серверу") \ + X(p2p_connected, u8"对等连接已建立", "P2P Connected", u8"P2P подключено") \ + X(p2p_disconnected, u8"对等连接已断开", "P2P Disconnected", \ + u8"P2P отключено") \ + X(p2p_connecting, u8"正在建立对等连接...", "P2P Connecting ...", \ + u8"Подключение P2P...") \ + X(receiving_screen, u8"画面接收中...", "Receiving screen...", \ + u8"Получение изображения...") \ + X(p2p_failed, u8"对等连接失败", "P2P Failed", u8"Сбой P2P") \ + X(p2p_closed, u8"对等连接已关闭", "P2P closed", u8"P2P закрыто") \ + X(no_such_id, u8"无此ID", "No such ID", u8"ID не найден") \ + X(about, u8"关于", "About", u8"О программе") \ + X(notification, u8"通知", "Notification", u8"Уведомление") \ + X(new_version_available, u8"新版本可用", "New Version Available", \ + u8"Доступна новая версия") \ + X(version, u8"版本", "Version", u8"Версия") \ + X(release_date, u8"发布日期: ", "Release Date: ", u8"Дата релиза: ") \ + X(access_website, u8"访问官网: ", \ + "Access Website: ", u8"Официальный сайт: ") \ + X(update, u8"更新", "Update", u8"Обновить") \ + X(confirm_delete_connection, u8"确认删除此连接", \ + "Confirm to delete this connection", u8"Удалить это подключение?") \ + X(enable_autostart, u8"开机自启:", "Auto Start:", u8"Автозапуск:") \ + X(enable_daemon, u8"启用守护进程:", "Enable Daemon:", u8"Включить демон:") \ + X(takes_effect_after_restart, u8"重启后生效", "Takes effect after restart", \ + u8"Вступит в силу после перезапуска") \ + X(select_file, u8"选择文件发送", "Select File to Send", \ + u8"Выбрать файл для отправки") \ + X(file_transfer_progress, u8"文件传输进度", "File Transfer Progress", \ + u8"Прогресс передачи файлов") \ + X(queued, u8"队列中", "Queued", u8"В очереди") \ + X(sending, u8"正在传输", "Sending", u8"Передача") \ + X(completed, u8"已完成", "Completed", u8"Завершено") \ + X(failed, u8"失败", "Failed", u8"Ошибка") \ + X(controller, u8"控制端:", "Controller:", u8"Контроллер:") \ + X(file_transfer, u8"文件传输:", "File Transfer:", u8"Передача файлов:") \ + X(connection_status, u8"连接状态:", \ + "Connection Status:", u8"Состояние соединения:") \ + X(file_transfer_save_path, u8"文件接收保存路径:", \ + "File Transfer Save Path:", u8"Путь сохранения файлов:") \ + X(default_desktop, u8"桌面", "Desktop", u8"Рабочий стол") \ + X(minimize_to_tray, u8"退出时最小化到系统托盘:", \ + "Minimize on Exit:", u8"Сворачивать в трей при выходе:") \ + X(resolution, u8"分辨率", "Res", u8"Разрешение") \ + X(connection_mode, u8"连接模式", "Mode", u8"Режим") \ + X(connection_mode_direct, u8"直连", "Direct", u8"Прямой") \ + X(connection_mode_relay, u8"中继", "Relay", u8"Релейный") \ + X(online, u8"在线", "Online", u8"Онлайн") \ + X(offline, u8"离线", "Offline", u8"Офлайн") \ + X(device_offline, u8"设备离线", "Device Offline", u8"Устройство офлайн") \ + X(request_permissions, u8"权限请求", "Request Permissions", \ + u8"Запрос разрешений") \ + X(screen_recording_permission, u8"屏幕录制权限", \ + "Screen Recording Permission", u8"Разрешение на запись экрана") \ + X(accessibility_permission, u8"辅助功能权限", "Accessibility Permission", \ + u8"Разрешение специальных возможностей") \ + X(permission_required_message, u8"该应用需要授权以下权限:", \ + "The application requires the following permissions:", \ + u8"Для работы приложения требуются следующие разрешения:") \ X(exit_program, u8"退出", "Exit", u8"Выход") inline constexpr TranslationRow kTranslationRows[] = { diff --git a/src/gui/toolbars/control_bar.cpp b/src/gui/toolbars/control_bar.cpp index b89d676..197efaa 100644 --- a/src/gui/toolbars/control_bar.cpp +++ b/src/gui/toolbars/control_bar.cpp @@ -15,6 +15,22 @@ namespace crossdesk { +namespace { + +void ShowControlBarTooltip(const std::string& text) { + if (!ImGui::IsItemHovered() || text.empty()) { + return; + } + + ImGui::BeginTooltip(); + ImGui::SetWindowFontScale(0.5f); + ImGui::Text("%s", text.c_str()); + ImGui::SetWindowFontScale(1.0f); + ImGui::EndTooltip(); +} + +} // namespace + int CountDigits(int number) { if (number == 0) return 1; return (int)std::floor(std::log10(std::abs(number))) + 1; @@ -162,6 +178,8 @@ int Render::ControlBar(std::shared_ptr& props) { ImVec2 btn_min = ImGui::GetItemRectMin(); ImVec2 btn_size_actual = ImGui::GetItemRectSize(); + ShowControlBarTooltip( + localization::select_display[localization_language_index_]); props->display_selectable_hovered_ = false; if (ImGui::BeginPopup("display")) { @@ -185,12 +203,12 @@ int Render::ControlBar(std::shared_ptr& props) { ImGui::EndPopup(); } - ImGui::SetWindowFontScale(0.5f); + ImGui::SetWindowFontScale(0.4f); ImVec2 text_size = ImGui::CalcTextSize( std::to_string(props->selected_display_ + 1).c_str()); ImVec2 text_pos = - ImVec2(btn_min.x + (btn_size_actual.x - text_size.x) * 0.5f, - btn_min.y + (btn_size_actual.y - text_size.y) * 0.35f); + ImVec2(btn_min.x + (btn_size_actual.x - text_size.x) * 0.53f, + btn_min.y + (btn_size_actual.y - text_size.y) * 0.33f); ImGui::GetWindowDrawList()->AddText( text_pos, IM_COL32(0, 0, 0, 255), std::to_string(props->selected_display_ + 1).c_str()); @@ -218,25 +236,14 @@ int Render::ControlBar(std::shared_ptr& props) { if (ImGui::Button(shortcut.c_str(), ImVec2(button_width, button_height))) { ImGui::OpenPopup("shortcut"); } - - if (ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); - ImGui::SetWindowFontScale(0.5f); - ImGui::Text( - "%s", - localization::send_shortcut[localization_language_index_].c_str()); - ImGui::SetWindowFontScale(1.0f); - ImGui::EndTooltip(); - } + ShowControlBarTooltip( + localization::send_shortcut[localization_language_index_]); props->shortcut_selectable_hovered_ = false; if (ImGui::BeginPopup("shortcut")) { ImGui::SetWindowFontScale(0.5f); - std::string sas_label = - "Ctrl+Alt+Del - " + - localization::send_sas[localization_language_index_]; - std::string lock_label = - "Win+L - " + localization::lock_remote[localization_language_index_]; + std::string sas_label = "Ctrl+Alt+Del"; + std::string lock_label = "Win+L"; if (ImGui::Selectable(sas_label.c_str())) { send_service_command(ServiceCommandFlag::send_sas, "SAS"); } @@ -268,6 +275,12 @@ int Render::ControlBar(std::shared_ptr& props) { : localization::control_mouse[localization_language_index_]; } } + const bool mouse_button_hovered = ImGui::IsItemHovered(); + const std::string mouse_tooltip = + props->enable_mouse_control_ + ? localization::release_mouse[localization_language_index_] + : localization::control_mouse[localization_language_index_]; + ShowControlBarTooltip(mouse_tooltip); if (!props->enable_mouse_control_) { draw_list->AddLine(ImVec2(disable_mouse_x, disable_mouse_y), @@ -280,8 +293,8 @@ int Render::ControlBar(std::shared_ptr& props) { ImVec2( mouse_x + button_width - line_padding - line_thickness * 0.7f, mouse_y + button_height - line_padding + line_thickness * 0.7f), - ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255) - : IM_COL32(179, 213, 253, 255), + mouse_button_hovered ? IM_COL32(66, 150, 250, 255) + : IM_COL32(179, 213, 253, 255), line_thickness); } @@ -313,6 +326,12 @@ int Render::ControlBar(std::shared_ptr& props) { props->control_data_label_.c_str()); } } + const bool audio_button_hovered = ImGui::IsItemHovered(); + const std::string audio_tooltip = + props->audio_capture_button_pressed_ + ? localization::mute[localization_language_index_] + : localization::audio_capture[localization_language_index_]; + ShowControlBarTooltip(audio_tooltip); if (!props->audio_capture_button_pressed_) { draw_list->AddLine(ImVec2(disable_audio_x, disable_audio_y), @@ -325,8 +344,8 @@ int Render::ControlBar(std::shared_ptr& props) { ImVec2( audio_x + button_width - line_padding - line_thickness * 0.7f, audio_y + button_height - line_padding + line_thickness * 0.7f), - ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255) - : IM_COL32(179, 213, 253, 255), + audio_button_hovered ? IM_COL32(66, 150, 250, 255) + : IM_COL32(179, 213, 253, 255), line_thickness); } @@ -339,6 +358,8 @@ int Render::ControlBar(std::shared_ptr& props) { std::string path = OpenFileDialog(title); ProcessSelectedFile(path, props, file_label_); } + ShowControlBarTooltip( + localization::select_file[localization_language_index_]); ImGui::SameLine(); // net traffic stats button @@ -363,6 +384,12 @@ int Render::ControlBar(std::shared_ptr& props) { : localization::show_net_traffic_stats [localization_language_index_]; } + const std::string net_traffic_stats_tooltip = + props->net_traffic_stats_button_pressed_ + ? localization::hide_net_traffic_stats[localization_language_index_] + : localization::show_net_traffic_stats + [localization_language_index_]; + ShowControlBarTooltip(net_traffic_stats_tooltip); if (button_color_style_pushed) { ImGui::PopStyleColor(); @@ -389,6 +416,11 @@ int Render::ControlBar(std::shared_ptr& props) { } props->reset_control_bar_pos_ = true; } + const std::string fullscreen_tooltip = + fullscreen_button_pressed_ + ? localization::exit_fullscreen[localization_language_index_] + : localization::fullscreen[localization_language_index_]; + ShowControlBarTooltip(fullscreen_tooltip); ImGui::SameLine(); // close button @@ -398,6 +430,8 @@ int Render::ControlBar(std::shared_ptr& props) { ImVec2(button_width, button_height))) { CleanupPeer(props); } + ShowControlBarTooltip( + localization::disconnect[localization_language_index_]); ImGui::SameLine(); @@ -427,6 +461,10 @@ int Render::ControlBar(std::shared_ptr& props) { : ICON_FA_ANGLE_RIGHT) : (props->is_control_bar_in_left_ ? ICON_FA_ANGLE_RIGHT : ICON_FA_ANGLE_LEFT); + const std::string control_bar_tooltip = + props->control_bar_expand_ + ? localization::collapse_control_bar[localization_language_index_] + : localization::expand_control_bar[localization_language_index_]; if (ImGui::Button(control_bar.c_str(), ImVec2(button_height * 0.6f, button_height))) { props->control_bar_expand_ = !props->control_bar_expand_; @@ -438,6 +476,7 @@ int Render::ControlBar(std::shared_ptr& props) { props->net_traffic_stats_button_pressed_ = false; } } + ShowControlBarTooltip(control_bar_tooltip); if (props->net_traffic_stats_button_pressed_ && props->control_bar_expand_) { NetTrafficStats(props); diff --git a/tests/display_popup_hover_state_test.cpp b/tests/display_popup_hover_state_test.cpp index 324e11e..e14b424 100644 --- a/tests/display_popup_hover_state_test.cpp +++ b/tests/display_popup_hover_state_test.cpp @@ -39,6 +39,35 @@ bool ExpectContains(const char* name, const std::string& value, return false; } +bool ExpectNotContains(const char* name, const std::string& value, + const std::string& unexpected) { + if (value.find(unexpected) == std::string::npos) { + return true; + } + + std::cerr << name << " contains unexpected text: " << unexpected << "\n"; + return false; +} + +bool ExpectContainsAtLeast(const char* name, const std::string& value, + const std::string& expected, size_t min_count) { + size_t count = 0; + size_t pos = 0; + while ((pos = value.find(expected, pos)) != std::string::npos) { + ++count; + pos += expected.size(); + } + + if (count >= min_count) { + return true; + } + + std::cerr << name << " expected at least " << min_count + << " occurrences of: " << expected << ", found " << count + << "\n"; + return false; +} + bool ExpectResetBeforeDisplayPopup(const std::string& value) { const std::string reset = "props->display_selectable_hovered_ = false;"; const std::string popup = "ImGui::BeginPopup(\"display\")"; @@ -93,5 +122,62 @@ int main() { ok &= ExpectContains("control_bar.cpp", control_bar, "props->shortcut_selectable_hovered_ ="); ok &= ExpectResetBeforeShortcutPopup(control_bar); + ok &= ExpectContains("control_bar.cpp", control_bar, + "void ShowControlBarTooltip(const std::string& text)"); + ok &= ExpectContainsAtLeast("control_bar.cpp", control_bar, + "ShowControlBarTooltip(", 10); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::select_display" + "[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::send_shortcut" + "[localization_language_index_]"); + ok &= ExpectNotContains("control_bar.cpp", control_bar, + "ShowControlBarTooltip(" + "props->mouse_control_button_label_)"); + ok &= ExpectNotContains("control_bar.cpp", control_bar, + "ShowControlBarTooltip(" + "props->audio_capture_button_label_)"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::select_file" + "[localization_language_index_]"); + ok &= ExpectNotContains("control_bar.cpp", control_bar, + "ShowControlBarTooltip(" + "props->net_traffic_stats_button_label_)"); + ok &= ExpectNotContains("control_bar.cpp", control_bar, + "ShowControlBarTooltip(" + "props->fullscreen_button_label_)"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::release_mouse" + "[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::control_mouse" + "[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::audio_capture" + "[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::mute[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::hide_net_traffic_stats" + "[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::show_net_traffic_stats" + "[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::exit_fullscreen" + "[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::fullscreen" + "[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::disconnect" + "[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::expand_control_bar" + "[localization_language_index_]"); + ok &= ExpectContains("control_bar.cpp", control_bar, + "localization::collapse_control_bar" + "[localization_language_index_]"); return ok ? 0 : 1; }