mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-03-24 00:57:33 +08:00
[feat] use tooltips to display server-side file transfer status information
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "clipboard.h"
|
||||
@@ -416,8 +417,46 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size,
|
||||
rate_bps = state->file_send_rate_bps_.load();
|
||||
}
|
||||
} else {
|
||||
// Global transfer: no per-connection bitrate, keep existing value.
|
||||
rate_bps = state->file_send_rate_bps_.load();
|
||||
// Global transfer: no per-connection bitrate available.
|
||||
// Estimate send rate from ACKed bytes delta over time.
|
||||
const uint32_t current_rate = state->file_send_rate_bps_.load();
|
||||
uint32_t estimated_rate_bps = 0;
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
|
||||
uint64_t last_bytes = 0;
|
||||
std::chrono::steady_clock::time_point last_time;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state->file_transfer_mutex_);
|
||||
last_bytes = state->file_send_last_bytes_;
|
||||
last_time = state->file_send_last_update_time_;
|
||||
}
|
||||
|
||||
if (state->file_sending_.load() && ack.acked_offset >= last_bytes) {
|
||||
const uint64_t delta_bytes = ack.acked_offset - last_bytes;
|
||||
const double delta_seconds =
|
||||
std::chrono::duration<double>(now - last_time).count();
|
||||
|
||||
if (delta_seconds > 0.0 && delta_bytes > 0) {
|
||||
const double bps =
|
||||
(static_cast<double>(delta_bytes) * 8.0) / delta_seconds;
|
||||
if (bps > 0.0) {
|
||||
const double capped =
|
||||
(std::min)(bps, static_cast<double>(
|
||||
(std::numeric_limits<uint32_t>::max)()));
|
||||
estimated_rate_bps = static_cast<uint32_t>(capped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (estimated_rate_bps > 0 && current_rate > 0) {
|
||||
// 70% old + 30% new for smoother display
|
||||
rate_bps = static_cast<uint32_t>(current_rate * 0.7 +
|
||||
estimated_rate_bps * 0.3);
|
||||
} else if (estimated_rate_bps > 0) {
|
||||
rate_bps = estimated_rate_bps;
|
||||
} else {
|
||||
rate_bps = current_rate;
|
||||
}
|
||||
}
|
||||
|
||||
state->file_send_rate_bps_ = rate_bps;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -9,6 +11,39 @@
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
namespace {
|
||||
int CountDigits(int number) {
|
||||
if (number == 0) return 1;
|
||||
return (int)std::floor(std::log10(std::abs(number))) + 1;
|
||||
}
|
||||
|
||||
void BitrateDisplay(uint32_t bitrate) {
|
||||
const int num_of_digits = CountDigits(static_cast<int>(bitrate));
|
||||
if (num_of_digits <= 3) {
|
||||
ImGui::Text("%u bps", bitrate);
|
||||
} else if (num_of_digits > 3 && num_of_digits <= 6) {
|
||||
ImGui::Text("%u kbps", bitrate / 1000);
|
||||
} else {
|
||||
ImGui::Text("%.1f mbps", bitrate / 1000000.0f);
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatBytes(uint64_t bytes) {
|
||||
char buf[64];
|
||||
if (bytes < 1024ULL) {
|
||||
std::snprintf(buf, sizeof(buf), "%llu B", (unsigned long long)bytes);
|
||||
} else if (bytes < 1024ULL * 1024ULL) {
|
||||
std::snprintf(buf, sizeof(buf), "%.2f KB", bytes / 1024.0);
|
||||
} else if (bytes < 1024ULL * 1024ULL * 1024ULL) {
|
||||
std::snprintf(buf, sizeof(buf), "%.2f MB", bytes / (1024.0 * 1024.0));
|
||||
} else {
|
||||
std::snprintf(buf, sizeof(buf), "%.2f GB",
|
||||
bytes / (1024.0 * 1024.0 * 1024.0));
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int Render::ServerWindow() {
|
||||
ImGui::SetNextWindowSize(ImVec2(server_window_width_, server_window_height_),
|
||||
ImGuiCond_Always);
|
||||
@@ -235,6 +270,75 @@ int Render::RemoteClientInfoWindow() {
|
||||
ProcessSelectedFile(path, nullptr, file_label_, selected_server_remote_id_);
|
||||
}
|
||||
|
||||
if (file_transfer_.file_transfer_window_visible_) {
|
||||
ImGui::SameLine();
|
||||
const bool is_sending = file_transfer_.file_sending_.load();
|
||||
|
||||
if (is_sending) {
|
||||
// Simple animation: cycle icon every 0.5s while sending.
|
||||
static const char* kFileTransferIcons[] = {ICON_FA_ANGLE_UP,
|
||||
ICON_FA_ANGLES_UP};
|
||||
const int icon_index = static_cast<int>(ImGui::GetTime() / 0.5) %
|
||||
(static_cast<int>(sizeof(kFileTransferIcons) /
|
||||
sizeof(kFileTransferIcons[0])));
|
||||
ImGui::Text("%s", kFileTransferIcons[icon_index]);
|
||||
} else {
|
||||
// Completed.
|
||||
ImGui::Text("%s", ICON_FA_CHECK);
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
const uint64_t sent_bytes = file_transfer_.file_sent_bytes_.load();
|
||||
const uint64_t total_bytes = file_transfer_.file_total_bytes_.load();
|
||||
const uint32_t rate_bps = file_transfer_.file_send_rate_bps_.load();
|
||||
|
||||
float progress = 0.0f;
|
||||
if (total_bytes > 0) {
|
||||
progress =
|
||||
static_cast<float>(sent_bytes) / static_cast<float>(total_bytes);
|
||||
progress = (std::max)(0.0f, (std::min)(1.0f, progress));
|
||||
}
|
||||
|
||||
std::string current_file_name;
|
||||
const uint32_t current_file_id = file_transfer_.current_file_id_.load();
|
||||
if (current_file_id != 0) {
|
||||
std::lock_guard<std::mutex> lock(
|
||||
file_transfer_.file_transfer_list_mutex_);
|
||||
for (const auto& info : file_transfer_.file_transfer_list_) {
|
||||
if (info.file_id == current_file_id) {
|
||||
current_file_name = info.file_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::BeginTooltip();
|
||||
if (server_windows_system_chinese_font_) {
|
||||
ImGui::PushFont(server_windows_system_chinese_font_);
|
||||
}
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (!current_file_name.empty()) {
|
||||
ImGui::Text("%s", current_file_name.c_str());
|
||||
}
|
||||
if (total_bytes > 0) {
|
||||
const std::string sent_str = FormatBytes(sent_bytes);
|
||||
const std::string total_str = FormatBytes(total_bytes);
|
||||
ImGui::Text("%s / %s", sent_str.c_str(), total_str.c_str());
|
||||
}
|
||||
|
||||
const float text_height = ImGui::GetTextLineHeight();
|
||||
char overlay[32];
|
||||
std::snprintf(overlay, sizeof(overlay), "%.1f%%", progress * 100.0f);
|
||||
ImGui::ProgressBar(progress, ImVec2(180.0f, text_height), overlay);
|
||||
BitrateDisplay(rate_bps);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
if (server_windows_system_chinese_font_) {
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
Reference in New Issue
Block a user