You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1217 lines
41 KiB

// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2017 Intel Corporation. All Rights Reserved.
#ifdef _MSC_VER
#ifndef NOMINMAX
#define NOMINMAX
#endif
#endif
#include "notifications.h"
#include <imgui_internal.h>
#include "model-views.h"
#include "os.h"
#include "viewer.h"
#include "metadata-helper.h"
#include "ux-window.h"
#include <thread>
#include <algorithm>
#include <regex>
#include <cmath>
#include <opengl3.h>
using namespace std;
using namespace chrono;
namespace rs2
{
notification_data::notification_data(std::string description,
rs2_log_severity severity,
rs2_notification_category category)
: _description(description),
_severity(severity),
_category(category)
{
_timestamp = (double)std::chrono::high_resolution_clock::now().time_since_epoch().count();
}
rs2_notification_category notification_data::get_category() const
{
return _category;
}
std::string notification_data::get_description() const
{
return _description;
}
double notification_data::get_timestamp() const
{
return _timestamp;
}
rs2_log_severity notification_data::get_severity() const
{
return _severity;
}
notification_model::notification_model()
{
custom_action = []{};
last_x = 500000;
last_y = 200;
message = "";
last_moved = std::chrono::system_clock::now();
created_time = std::chrono::system_clock::now();
last_interacted = std::chrono::system_clock::now() - std::chrono::milliseconds(500);
}
notification_model::notification_model(const notification_data& n)
: notification_model()
{
message = n.get_description();
timestamp = n.get_timestamp();
severity = n.get_severity();
created_time = std::chrono::system_clock::now();
last_interacted = std::chrono::system_clock::now() - std::chrono::milliseconds(500);
category = n.get_category();
}
double notification_model::get_age_in_ms(bool total) const
{
auto interacted = duration<double, milli>(last_interacted - created_time).count();
return duration<double, milli>(system_clock::now() - created_time).count() - (total ? 0.0 : interacted);
}
bool notification_model::interacted() const
{
return duration<double, milli>(system_clock::now() - last_interacted).count() < 100;
}
// Pops the N colors that were pushed in set_color_scheme
void notification_model::unset_color_scheme() const
{
ImGui::PopStyleColor(6);
}
void progress_bar::draw(ux_window & win, int bar_width, int progress)
{
auto now = system_clock::now();
auto ellapsed = duration_cast<milliseconds>(now - last_progress_time).count() / 1000.f;
auto new_progress = last_progress + ellapsed * progress_speed;
curr_progress_value = std::min(threshold_progress, new_progress);
if (last_progress != progress)
{
last_progress_time = system_clock::now();
int delta = progress - last_progress;
if (ellapsed > 0.f) progress_speed = delta / ellapsed;
threshold_progress = float(std::min(100, progress + delta));
last_progress = progress;
}
auto filled_w = (curr_progress_value * (bar_width - 4)) / 100.f;
auto pos = ImGui::GetCursorScreenPos();
ImGui::GetWindowDrawList()->AddRectFilled({ float(pos.x), float(pos.y) },
{ float(pos.x + bar_width), float(pos.y + 20) }, ImColor(black));
if (curr_progress_value > 0.f)
{
for (int i = 20; i >= 0; i -= 2)
{
auto a = curr_progress_value / 100.f;
ImGui::GetWindowDrawList()->AddRectFilled({ float(pos.x + 3 - i), float(pos.y + 3 - i) },
{ float(pos.x + filled_w + i), float(pos.y + 17 + i) },
ImColor(alpha(light_blue, sqrt(a) * 0.02f)), float(i));
}
ImGui::GetWindowDrawList()->AddRectFilled({ float(pos.x + 3), float(pos.y + 3) },
{ float(pos.x + filled_w), float(pos.y + 17) }, ImColor(light_blue));
ImGui::GetWindowDrawList()->AddRectFilledMultiColor({ float(pos.x + 5), float(pos.y + 5) },
{ float(pos.x + filled_w), float(pos.y + 15) },
ImColor(saturate(light_blue, 1.f)), ImColor(saturate(light_blue, 0.9f)),
ImColor(saturate(light_blue, 0.8f)), ImColor(saturate(light_blue, 1.f)));
rs2::rect pbar{ float(pos.x + 3), float(pos.y + 3), float(bar_width), 17.f };
auto mouse = win.get_mouse();
if (pbar.contains(mouse.cursor))
{
std::string progress_str = rsutils::string::from() << progress << "%";
ImGui::SetTooltip("%s", progress_str.c_str());
}
}
ImGui::SetCursorScreenPos({ float(pos.x), float(pos.y + 25) });
}
void process_notification_model::draw_progress_bar(ux_window & win, int bar_width)
{
auto progress = update_manager->get_progress();
_progress_bar.draw(win, bar_width, progress);
}
/* Sets color scheme for notifications, must be used with unset_color_scheme to pop all colors in the end
Parameter t indicates the transparency of the nofication interface */
void notification_model::set_color_scheme(float t) const
{
ImVec4 c;
ImGui::PushStyleColor(ImGuiCol_Button, saturate(c, 1.3f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, saturate(c, 0.9f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, saturate(c, 1.5f));
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, white);
c = alpha(white, 1 - t);
ImGui::PushStyleColor(ImGuiCol_Text, c);
if (severity == RS2_LOG_SEVERITY_ERROR ||
severity == RS2_LOG_SEVERITY_WARN)
{
c = alpha(dark_red, 1 - t);
ImGui::PushStyleColor(ImGuiCol_WindowBg, c);
}
else
{
c = alpha(saturate(grey, 0.7f), 1 - t);
ImGui::PushStyleColor(ImGuiCol_WindowBg, c);
}
}
int notification_model::get_max_lifetime_ms() const
{
return 10000;
}
void notification_model::draw_text(const char* msg, int x, int y, int h)
{
std::string text_name = rsutils::string::from() << "##notification_text_" << index;
ImGui::PushTextWrapPos(x + width - 100.f);
ImGui::PushStyleColor(ImGuiCol_FrameBg, transparent);
ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, transparent);
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrab, transparent);
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabActive, transparent);
ImGui::PushStyleColor(ImGuiCol_ScrollbarGrabHovered, transparent);
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, regular_blue);
if (enable_click) ImGui::Text("%s", msg);
else ImGui::InputTextMultiline(text_name.c_str(), const_cast<char*>(msg),
strlen(msg) + 1, { float(width - (count > 1 ? 40 : 10)), float(h) },
ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleColor(6);
ImGui::PopTextWrapPos();
if (ImGui::IsItemHovered())
{
last_interacted = system_clock::now();
}
}
std::string notification_model::get_title()
{
auto title = message;
auto parts = split_string(title, '`');
if (parts.size() > 1) title = parts[0];
return title;
}
int notification_model::calc_height()
{
auto title = get_title();
auto lines = static_cast<int>(std::count(title.begin(), title.end(), '\n') + 1);
return int((lines + 1) * ImGui::GetTextLineHeight() + 5);
}
void process_notification_model::draw_pre_effect(int x, int y)
{
// TODO: Make polymorphic
if (update_state == 2)
{
auto k = duration_cast<milliseconds>(system_clock::now() - _progress_bar.last_progress_time).count() / 500.f;
if (k <= 1.f)
{
auto size = 100;
k = pow(1.f - smoothstep(static_cast<float>(k), 0.f, 1.f), 2.f);
ImGui::GetWindowDrawList()->AddRectFilled({ float(x - size * k), float(y - size * k) },
{ float(x + width + size * k), float(y + height + size * k) },
ImColor(alpha(white, (1.f - k) / 2)), (size * k) / 2);
}
}
}
void notification_model::draw_content(ux_window& win, int x, int y, float t, std::string& error_message)
{
draw_text(get_title().c_str(), x, y, height - 35);
}
void rs2::notification_model::draw_dismiss(ux_window & win, int x, int y)
{
ImGui::SetCursorScreenPos({ float(x + width - 105), float(y + height - 25) });
string id = rsutils::string::from() << "Dismiss" << "##" << index;
ImGui::PushStyleColor(ImGuiCol_Text, black);
ImGui::PushStyleColor(ImGuiCol_PopupBg, almost_white_bg);
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, light_blue);
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, white);
ImGui::PushFont(win.get_font());
ImGui::SetNextWindowPos({ float(x + width - 125), float(y + height - 25) });
ImGui::SetNextWindowSize({ 120, 70 });
std::string dismiss_popup = rsutils::string::from() << "Dismiss Options" << "##" << index;
if (ImGui::BeginPopup(dismiss_popup.c_str()))
{
if (ImGui::Selectable("Just this time"))
{
dismiss(true);
}
if (ImGui::Selectable("Remind me later"))
{
delay(7);
dismiss(true);
}
if (ImGui::Selectable("Don't show again"))
{
delay(1000);
dismiss(true);
}
ImGui::EndPopup();
}
ImGui::PopFont();
ImGui::PopStyleColor(4);
if (ImGui::Button(id.c_str(), { 100, 20 }))
{
if (enable_complex_dismiss)
ImGui::OpenPopup(dismiss_popup.c_str());
else dismiss(true);
}
if (ImGui::IsItemHovered())
{
win.link_hovered();
}
}
std::function<void()> notification_model::draw(ux_window& win, int w, int y,
std::shared_ptr<notification_model>& selected, std::string& error_message)
{
std::function<void()> action;
while(dispatch_queue.try_dequeue(&action)) action();
std::function<void()> follow_up = []{};
if (visible)
{
auto stack = std::min(count, max_stack);
auto x = w - width - 10;
if (dismissed)
{
x = w + width;
}
if (!animating && (fabs(x - last_x) > 1.f || fabs(y - last_y) > 1.f))
{
if (last_x > 100000)
{
last_x = x + 500.f;
last_y = float(y);
}
last_moved = system_clock::now();
animating = true;
}
auto elapsed = duration<double, milli>(system_clock::now() - last_moved).count();
auto s = smoothstep(static_cast<float>(elapsed / 250.f), 0.0f, 1.0f);
if (s < 1.f)
{
x = int(s * x + (1 - s) * last_x);
y = int(s * y + (1 - s) * last_y);
}
else
{
last_x = float(x); last_y = float(y);
animating = false;
if (dismissed && !expanded) to_close = true;
}
auto ms = get_age_in_ms() / get_max_lifetime_ms();
auto t = smoothstep(static_cast<float>(ms), 0.8f, 1.f);
if (pinned) t = 0.f;
set_color_scheme(t);
height = calc_height();
auto c = ImGui::GetStyle().Colors[ImGuiCol_WindowBg];
c.w = smoothstep(static_cast<float>(get_age_in_ms(true) / 200.f), 0.0f, 1.0f);
c.w = std::min(c.w, 1.f - t);
draw_pre_effect(x, y);
for (int i = stack - 1; i >= 0; i--)
{
auto ccopy = alpha(c, (0.9f * c.w) / (i + 1));
ImVec4 shadow{ 0.1f, 0.1f, 0.1f, 0.1f };
ImGui::GetWindowDrawList()->AddRectFilled({ float(x + 2 + i * stack_offset), float(y + 2 + i * stack_offset) },
{ float(x + 2 + width + i * stack_offset), float(y + 2 + height + i * stack_offset) }, ImColor(shadow));
ImGui::GetWindowDrawList()->AddRectFilledMultiColor({ float(x + i * stack_offset), float(y + i * stack_offset) },
{ float(x + width + i * stack_offset), float(y + height + i * stack_offset) },
ImColor(saturate(ccopy, 0.9f)), ImColor(saturate(ccopy, 0.95f)),
ImColor(saturate(ccopy, 1.2f)), ImColor(saturate(ccopy, 1.1f)));
ImGui::GetWindowDrawList()->AddRect({ float(x + i * stack_offset), float(y + i * stack_offset) },
{ float(x + width + i * stack_offset), float(y + height + i * stack_offset) }, ImColor(saturate(ccopy, 0.5f)));
}
ImGui::SetCursorScreenPos({ float(x), float(y) });
if (enable_click)
{
std::string button_name = rsutils::string::from() << "##" << index;
ImGui::PushStyleColor(ImGuiCol_Button, transparent);
if (ImGui::Button(button_name.c_str(), { (float)width, (float)height }))
{
follow_up = custom_action;
dismiss(false);
}
if (ImGui::IsItemHovered())
win.link_hovered();
ImGui::PopStyleColor();
}
if (count > 1)
{
std::string count_str = rsutils::string::from() << "x " << count;
ImGui::SetCursorScreenPos({ float(x + width - 22 - count_str.size() * 5), float(y + 7) });
ImGui::Text("%s", count_str.c_str());
}
ImGui::SetCursorScreenPos({ float(x + 5), float(y + 5) });
draw_content(win, x, y, t, error_message);
if (enable_expand)
{
ImGui::SetCursorScreenPos({ float(x + 5), float(y + height - 25) });
ImGui::PushFont(win.get_large_font());
ImGui::PushStyleColor(ImGuiCol_Button, transparent);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, transparent);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, transparent);
string id = rsutils::string::from() << textual_icons::dotdotdot << "##" << index;
if (ImGui::Button(id.c_str()))
{
selected = shared_from_this();
}
if (ImGui::IsItemHovered())
win.link_hovered();
ImGui::PopStyleColor(3);
ImGui::PopFont();
}
if (enable_dismiss)
{
draw_dismiss(win, x, y);
}
unset_color_scheme();
}
if (expanded)
{
draw_expanded(win, error_message);
}
return follow_up;
}
std::shared_ptr<notification_model> notifications_model::add_notification(const notification_data& n)
{
return add_notification(n, []{}, false);
}
std::shared_ptr<notification_model> notifications_model::add_notification(const notification_data& n,
std::function<void()> custom_action, bool use_custom_action)
{
std::shared_ptr<notification_model> result = nullptr;
{
using namespace std;
using namespace chrono;
lock_guard<recursive_mutex> lock(m); // need to protect the pending_notifications queue because the insertion of notifications
// done from the notifications callback and proccesing and removing of old notifications done from the main thread
for (auto&& nm : pending_notifications)
{
if (nm->category == n.get_category() && nm->message == n.get_description())
{
nm->last_interacted = std::chrono::system_clock::now();
nm->count++;
return nm;
}
}
auto m = std::make_shared<notification_model>(n);
m->index = index++;
result = m;
m->timestamp = duration<double, milli>(system_clock::now().time_since_epoch()).count();
if (n.get_category() == RS2_NOTIFICATION_CATEGORY_COUNT)
{
m->pinned = true;
}
if (use_custom_action)
{
m->custom_action = custom_action;
m->enable_click = true;
m->enable_expand = false;
m->enable_dismiss = false;
}
pending_notifications.push_back(m);
if (pending_notifications.size() > (size_t)MAX_SIZE)
{
auto it = pending_notifications.begin();
while (it != pending_notifications.end() && (*it)->pinned) it++;
if (it != pending_notifications.end())
pending_notifications.erase(it);
}
}
output.add_log(n.get_severity(), __FILE__, __LINE__, n.get_description());
return result;
}
void notifications_model::add_notification(std::shared_ptr<notification_model> model)
{
{
using namespace std;
using namespace chrono;
lock_guard<recursive_mutex> lock(m); // need to protect the pending_notifications queue because the insertion of notifications
// done from the notifications callback and proccesing and removing of old notifications done from the main thread
model->index = index++;
model->timestamp = duration<double, milli>(system_clock::now().time_since_epoch()).count();
pending_notifications.push_back(model);
if (pending_notifications.size() > (size_t)MAX_SIZE)
{
auto it = pending_notifications.begin();
while (it != pending_notifications.end() && (*it)->pinned) it++;
if (it != pending_notifications.end())
pending_notifications.erase(it);
}
}
output.add_log(model->severity, __FILE__, __LINE__, model->get_title());
}
bool notifications_model::draw(ux_window& win, int w, int h, std::string& error_message)
{
bool modal_notification_found = false;
ImGui::PushFont(win.get_font());
std::vector<std::function<void()>> follow_up;
{
bool pinned_drawn = false;
std::lock_guard<std::recursive_mutex> lock(m);
if (pending_notifications.size() > 0)
{
// loop over all notifications, remove "old" ones
pending_notifications.erase(std::remove_if(std::begin(pending_notifications),
std::end(pending_notifications),
[&](std::shared_ptr<notification_model>& n)
{
if (n->snoozed && n->pinned)
{
n->dismissed = false;
n->to_close = false;
return true;
}
return ((n->get_age_in_ms() > n->get_max_lifetime_ms() &&
!n->pinned && !n->expanded) || n->to_close);
}), end(pending_notifications));
auto height = 60;
for (auto& noti : pending_notifications)
{
modal_notification_found = modal_notification_found || noti->expanded;
if (pinned_drawn && noti->pinned && !noti->forced)
{
continue;
}
follow_up.push_back(noti->draw(win, w, height, selected, error_message));
if (noti->pinned) pinned_drawn = true;
if (noti->visible)
height += noti->height + 4 +
std::min(noti->count, noti->max_stack) * noti->stack_offset;
}
}
ImGui::PushStyleColor(ImGuiCol_WindowBg, { 0, 0, 0, 0 });
//ImGui::Begin("Notification parent window", nullptr, flags);
//selected.set_color_scheme(0.f);
ImGui::PushStyleColor(ImGuiCol_Text, light_grey);
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, white);
ImGui::PushStyleColor(ImGuiCol_PopupBg, sensor_bg);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(3, 3));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 1);
if (selected && selected->message != "")
ImGui::OpenPopup("Notification from Hardware");
if (ImGui::BeginPopupModal("Notification from Hardware", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::Text("Received the following notification:");
auto parts = split_string(selected->message, '`');
std::stringstream ss;
ss << "Timestamp: "
<< std::fixed << selected->timestamp
<< "\nSeverity: " << selected->severity
<< "\nDescription: ";
for (auto&& part : parts) ss << part << "\n";
auto s = ss.str();
ImGui::PushStyleColor(ImGuiCol_TextSelectedBg, regular_blue);
ImGui::InputTextMultiline("notification", const_cast<char*>(s.c_str()),
s.size() + 1, { 500,100 }, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleColor();
if (ImGui::Button("OK", ImVec2(120, 0)))
{
selected->message = "";
selected = nullptr;
ImGui::CloseCurrentPopup();
}
else
{
std::string clip = "";
auto lines = split_string(selected->message, '\n');
for (auto & line : lines)
{
if (line.size() && line[0] == '$') clip += line.substr(2) + "\n";
}
if (clip != "")
{
ImGui::SameLine();
if (ImGui::Button(" Copy Commands "))
{
glfwSetClipboardString(win, clip.c_str());
}
if (ImGui::IsItemActive())
ImGui::SetTooltip("Paste the copied commands to a terminal and enter your password to run");
}
}
ImGui::EndPopup();
}
ImGui::PopStyleVar(2);
ImGui::PopStyleColor(3);
ImGui::PopStyleColor();
}
for (auto& action : follow_up)
{
try
{
action();
}
catch (...) {}
}
ImGui::PopFont();
return modal_notification_found;
}
version_upgrade_model::version_upgrade_model(int version)
: process_notification_model(nullptr), _version(version)
{
enable_expand = false;
enable_dismiss = true;
update_state = 2;
//pinned = true;
_progress_bar.last_progress_time = system_clock::now();
}
void version_upgrade_model::set_color_scheme(float t) const
{
notification_model::set_color_scheme(t);
ImGui::PopStyleColor(1);
auto c = alpha(sensor_bg, 1 - t);
ImGui::PushStyleColor(ImGuiCol_WindowBg, c);
}
void version_upgrade_model::draw_content(ux_window& win, int x, int y, float t, std::string& error_message)
{
if (_first)
{
_progress_bar.last_progress_time = system_clock::now();
_first = false;
}
ImGui::PushStyleColor(ImGuiCol_Text, alpha(light_grey, 1.f - t));
ImGui::PushFont(win.get_large_font());
ImGui::SetCursorScreenPos({ float(x + 20), float(y + 16) });
ImGui::Text("Welcome to"); ImGui::SameLine();
std::string txt = rsutils::string::from() << "librealsense " << RS2_API_VERSION_STR << "!";
ImGui::PushStyleColor(ImGuiCol_Text, alpha(white, 1.f - t));
ImGui::Text("%s", txt.c_str());
ImGui::PopStyleColor();
ImGui::PopFont();
ImGui::SetCursorScreenPos({ float(x + 17), float(y + 41) });
std::string link = rsutils::string::from() << "https://github.com/IntelRealSense/librealsense/wiki/Release-Notes#release-" << _version;
ImGui::PushStyleColor(ImGuiCol_Text, alpha(light_blue, 1.f - t));
if (ImGui::Button("What's new"))
{
open_url(link.c_str());
}
ImGui::PopStyleColor();
if (ImGui::IsItemHovered())
{
win.link_hovered();
ImGui::SetTooltip("Open the Release Notes. Internet connection is required");
}
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 5);
ImGui::Text("in this release?");
ImGui::PopStyleColor();
}
int version_upgrade_model::calc_height()
{
return 80;
}
void process_manager::log(std::string line)
{
std::lock_guard<std::mutex> lock(_log_lock);
_log += line + "\n";
}
void process_manager::reset()
{
_progress = 0;
_started = false;
_done = false;
_failed = false;
_last_error = "";
}
void process_manager::fail(std::string error)
{
_last_error = error;
_progress = 0;
log("\nERROR: " + error);
_failed = true;
}
void notification_model::invoke(std::function<void()> action)
{
// We use a weak_ptr for making sure the queue object is
// alive when we access it from the dispatcher thread
auto sptr_q = std::make_shared<single_consumer_queue<bool>> ();
std::weak_ptr<single_consumer_queue<bool>> wptr_q( sptr_q );
dispatch_queue.enqueue([wptr_q, &action](){
try
{
action();
auto q = wptr_q.lock();
if( ! q )
return;
q->enqueue( true );
}
catch(...)
{
auto q = wptr_q.lock();
if( ! q )
return;
q->enqueue(false);
}
});
bool res;
if (!sptr_q->dequeue(&res, 100000) || !res)
throw std::runtime_error("Invoke operation failed!");
}
void process_manager::start(invoker invoke)
{
auto cleanup = [invoke]() {
};
log( rsutils::string::from() << "Started " << _process_name << " process" );
auto me = shared_from_this();
std::weak_ptr<process_manager> ptr(me);
std::thread t([ptr, cleanup, invoke]() {
auto self = ptr.lock();
if (!self) return;
try
{
self->process_flow(cleanup, invoke);
}
catch (const error& e)
{
self->fail(error_to_string(e));
cleanup();
}
catch (const std::exception& ex)
{
self->fail(ex.what());
cleanup();
}
catch (...)
{
self->fail( rsutils::string::from() << "Unknown error during " << self->_process_name << " process!" );
cleanup();
}
});
t.detach();
_started = true;
}
void export_manager::process_flow(
std::function<void()> cleanup,
invoker invoke)
{
_progress = 5;
_exporter->process(_data);
_progress = 100;
_done = true;
}
void export_notification_model::draw_content(ux_window& win, int x, int y, float t, std::string& error_message)
{
using namespace std;
using namespace chrono;
ImGui::SetCursorScreenPos({ float(x + 9), float(y + 4) });
ImVec4 shadow{ 1.f, 1.f, 1.f, 0.1f };
ImGui::GetWindowDrawList()->AddRectFilled({ float(x), float(y) },
{ float(x + width), float(y + 25) }, ImColor(shadow));
if (update_state != STATE_COMPLETE)
{
ImGui::Text("Export in progress");
ImGui::SetCursorScreenPos({ float(x + 10), float(y + 35) });
ImGui::PushStyleColor(ImGuiCol_Text, alpha(light_grey, 1.f - t));
std::string s = "Saving 3D view to " + get_file_name( get_manager().get_filename() );
ImGui::Text("%s", s.c_str());
ImGui::PopStyleColor();
}
else
{
ImGui::Text("Export Completed");
ImGui::SetCursorScreenPos({ float(x + 10), float(y + 35) });
ImGui::PushFont(win.get_large_font());
std::string txt = rsutils::string::from() << textual_icons::throphy;
ImGui::Text("%s", txt.c_str());
ImGui::PopFont();
ImGui::SetCursorScreenPos({ float(x + 40), float(y + 35) });
std::string s = "Finished saving 3D view to " + get_file_name( get_manager().get_filename() );
ImGui::Text("%s", s.c_str());
}
ImGui::SetCursorScreenPos({ float(x + 5), float(y + height - 25) });
const auto bar_width = width - 115;
if (update_state == STATE_IN_PROGRESS)
{
if (update_manager->done())
{
update_state = STATE_COMPLETE;
pinned = false;
_progress_bar.last_progress_time = last_interacted = system_clock::now();
}
if (!expanded)
{
if (update_manager->failed())
{
update_manager->check_error(error_message);
update_state = STATE_FAILED;
pinned = false;
dismiss(false);
}
draw_progress_bar(win, bar_width);
ImGui::SetCursorScreenPos({ float(x + width - 105), float(y + height - 25) });
}
}
}
int export_notification_model::calc_height()
{
return 85;
}
void export_notification_model::set_color_scheme(float t) const
{
notification_model::set_color_scheme(t);
ImGui::PopStyleColor(1);
ImVec4 c;
if (update_state == STATE_COMPLETE)
{
c = alpha(saturate(light_blue, 0.7f), 1 - t);
ImGui::PushStyleColor(ImGuiCol_WindowBg, c);
}
else
{
c = alpha(sensor_bg, 1 - t);
ImGui::PushStyleColor(ImGuiCol_WindowBg, c);
}
}
export_notification_model::export_notification_model(std::shared_ptr<export_manager> manager)
: process_notification_model(manager)
{
enable_expand = false;
expanded = false;
if (expanded) visible = false;
message = "";
update_state = STATE_IN_PROGRESS;
this->severity = RS2_LOG_SEVERITY_INFO;
this->category = RS2_NOTIFICATION_CATEGORY_FIRMWARE_UPDATE_RECOMMENDED;
pinned = true;
}
metadata_warning_model::metadata_warning_model()
: notification_model()
{
enable_expand = false;
enable_dismiss = true;
pinned = true;
message = "Frame Metadata is a device feature allowing\n"
"software synchronization between different\n"
"camera streams.\n"
"It must be explicitly enabled on Windows OS\n";
}
void metadata_warning_model::set_color_scheme(float t) const
{
notification_model::set_color_scheme(t);
ImGui::PopStyleColor(1);
auto c = alpha(sensor_bg, 1 - t);
ImGui::PushStyleColor(ImGuiCol_WindowBg, c);
}
void metadata_warning_model::draw_content(ux_window& win, int x, int y, float t, std::string& error_message)
{
ImGui::SetCursorScreenPos({ float(x + 9), float(y + 4) });
ImVec4 shadow{ 1.f, 1.f, 1.f, 0.1f };
ImGui::GetWindowDrawList()->AddRectFilled({ float(x), float(y) },
{ float(x + width), float(y + 25) }, ImColor(shadow));
ImGui::Text("Frame Metadata Disabled");
ImGui::SetCursorScreenPos({ float(x + 5), float(y + 27) });
ImGui::PushStyleColor(ImGuiCol_Text, light_grey);
draw_text(get_title().c_str(), x, y, height - 50);
ImGui::PopStyleColor();
ImGui::SetCursorScreenPos({ float(x + 5), float(y + height - 25) });
auto sat = 1.f + sin(duration_cast<milliseconds>(system_clock::now() - created_time).count() / 700.f) * 0.1f;
ImGui::PushStyleColor(ImGuiCol_Button, saturate(sensor_header_light_blue, sat));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, saturate(sensor_header_light_blue, 1.5f));
std::string button_name = rsutils::string::from() << "Enable" << "##enable_metadata" << index;
const auto bar_width = width - 115;
if (ImGui::Button(button_name.c_str(), { float(bar_width), 20.f }))
{
metadata_helper::instance().enable_metadata();
dismiss(false);
}
ImGui::PopStyleColor(2);
if (ImGui::IsItemHovered())
{
ImGui::SetTooltip("%s", "Enables metadata on connected devices (you may be prompted for administrator privileges)");
}
}
bool notification_model::is_delayed() const
{
// Make sure we don't spam calibration remainders too often:
time_t rawtime;
time(&rawtime);
std::string str = rsutils::string::from() << "notifications." << delay_id << ".next";
long long next_time = config_file::instance().get_or_default(str.c_str(), (long long)0);
return rawtime < next_time;
}
void notification_model::delay(int days)
{
// Make sure we don't spam calibration remainders too often:
time_t rawtime;
time(&rawtime);
std::string str = rsutils::string::from() << "notifications." << delay_id << ".next";
config_file::instance().set(str.c_str(), (long long)(rawtime + days * 60 * 60 * 24));
}
void notification_model::reset_delay()
{
if( is_delayed() )
{
std::string str = rsutils::string::from() << "notifications." << delay_id << ".next";
config_file::instance().remove( str.c_str());
}
}
sw_recommended_update_alert_model::sw_recommended_update_alert_model(const std::string& current_version, const std::string& recommended_version, const std::string& recommended_version_link)
: notification_model(), _current_version(current_version), _recommended_version(recommended_version), _recommended_version_link(recommended_version_link)
{
enable_expand = false;
enable_dismiss = true;
pinned = true;
forced = true;
severity = RS2_LOG_SEVERITY_INFO;
message = "Current SW version: " + _current_version +"\n" +
"Recommended SW version: " + _recommended_version;
}
void sw_recommended_update_alert_model::set_color_scheme(float t) const
{
notification_model::set_color_scheme(t);
ImGui::PopStyleColor(1);
auto c = alpha(sensor_bg, 1 - t);
ImGui::PushStyleColor(ImGuiCol_WindowBg, c);
}
void sw_recommended_update_alert_model::draw_content(
ux_window& win, int x, int y, float t, std::string& error_message)
{
ImGui::SetCursorScreenPos({ float(x + 9), float(y + 4) });
ImVec4 shadow{ 1.f, 1.f, 1.f, 0.1f };
ImGui::GetWindowDrawList()->AddRectFilled({ float(x), float(y) },
{ float(x + width), float(y + 25) },
ImColor(shadow));
ImGui::Text("Software Update Recommended!");
ImGui::SetCursorScreenPos({ float(x + 5), float(y + 27) });
draw_text(get_title().c_str(), x, y , height - 50);
ImGui::SetCursorScreenPos({ float(x + 9), float(y + height - 77) });
ImGui::PushStyleColor(ImGuiCol_Text, alpha(light_grey, 1.f - t));
ImGui::Text("We strongly recommend you upgrade \nyour software\n");
ImGui::PopStyleColor();
ImGui::SetCursorScreenPos({ float(x + 5), float(y + height - 25) });
auto sat = 1.f
+ sin(duration_cast<milliseconds>(system_clock::now() - created_time).count()
/ 700.f)
* 0.1f;
ImGui::PushStyleColor(ImGuiCol_Button, saturate(sensor_header_light_blue, sat));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, saturate(sensor_header_light_blue, 1.5f));
const auto bar_width = width - 115;
ImGui::PushStyleColor(ImGuiCol_Text, alpha(white, 1.f - t));
std::string button_name = rsutils::string::from() << "Learn More..." << "##" << index;
if (ImGui::Button(button_name.c_str(), { float(bar_width), 20.f }))
{
bool should_dismiss = true;
try
{
open_url(_recommended_version_link.c_str());
}
catch (const exception& e)
{
error_message = e.what();
should_dismiss = false;
}
if (should_dismiss) dismiss(false);
}
ImGui::PopStyleColor();
if (ImGui::IsItemHovered())
{
win.link_hovered();
ImGui::SetTooltip("Internet connection required");
}
ImGui::PopStyleColor(2);
}
sw_update_up_to_date_model::sw_update_up_to_date_model()
: notification_model()
{
enable_expand = false;
enable_dismiss = false;
pinned = false;
forced = true;
severity = RS2_LOG_SEVERITY_INFO;
message = "SW/FW versions up to date";
}
void sw_update_up_to_date_model::set_color_scheme(float t) const
{
notification_model::set_color_scheme(t);
ImGui::PopStyleColor();
ImVec4 c;
c = alpha(saturate(light_blue, 0.7f), 1 - t);
ImGui::PushStyleColor(ImGuiCol_WindowBg, c);
}
void sw_update_up_to_date_model::draw_content(ux_window& win, int x, int y, float t, std::string& error_message)
{
using namespace std;
using namespace chrono;
ImGui::SetCursorScreenPos({ float(x + 9), float(y + 4) });
ImVec4 shadow{ 1.f, 1.f, 1.f, 0.1f };
ImGui::GetWindowDrawList()->AddRectFilled({ float(x), float(y) },
{ float(x + width), float(y + 25) }, ImColor(shadow));
ImGui::Text("Updates Status");
ImGui::SetCursorScreenPos({ float(x + 10), float(y + 35) });
ImGui::PushFont(win.get_large_font());
std::string txt = rsutils::string::from() << textual_icons::throphy;
ImGui::Text("%s", txt.c_str());
ImGui::PopFont();
ImGui::SetCursorScreenPos({ float(x + 40), float(y + 35) });
ImGui::Text("SW/FW Versions All Up To Date");
ImGui::SetCursorScreenPos({ float(x + 5), float(y + height - 25) });
}
ucal_disclaimer_model::ucal_disclaimer_model()
: notification_model()
{
enable_expand = false;
enable_dismiss = true;
enable_complex_dismiss = true; // Allow to inhibit the disclaimer
pinned = false;
}
void ucal_disclaimer_model::draw_content(ux_window& win, int x, int y, float t, std::string& error_message)
{
ImGui::SetCursorScreenPos({ float(x + 9), float(y + 4) });
ImGui::GetWindowDrawList()->AddRectFilled({ float(x), float(y) },
{ float(x + width), float(y + 25) }, ImColor(orange));
ImGui::Text("Self-Calibration - Disclaimer");
ImGui::SetCursorScreenPos({ float(x + 5), float(y + 27) });
ImGui::PushStyleColor(ImGuiCol_Text, light_grey);
std::string message = "For D415 please refer to the links for details:";
draw_text(message.c_str(), x, y, 30);
ImGui::PopStyleColor();
ImGui::SetCursorScreenPos({ float(x + 10), float(y + 47) });
hyperlink(win, "1. Self-Calibration Whitepaper", "https://dev.intelrealsense.com/docs/self-calibration-for-depth-cameras");
ImGui::SetCursorScreenPos({ float(x + 10), float(y + 67) });
hyperlink(win, "2. Firmware Releases / Errata", "https://dev.intelrealsense.com/docs/firmware-releases");
}
fl_cal_limitation_model::fl_cal_limitation_model()
: notification_model()
{
enable_expand = false;
enable_dismiss = true;
enable_complex_dismiss = false;
pinned = false;
message = "Focal-Length Calibration for this device\n"
" requires USB3 connection.\n"
"Please switch the connection port and rerun";
}
void fl_cal_limitation_model::draw_content(ux_window& win, int x, int y, float t, std::string& error_message)
{
ImGui::SetCursorScreenPos({ float(x + 9), float(y + 4) });
ImGui::GetWindowDrawList()->AddRectFilled({ float(x), float(y) },
{ float(x + width), float(y + 25) }, ImColor(grey));
ImGui::Text("User Notification");
ImGui::SetCursorScreenPos({ float(x + 5), float(y + 25) });
ImGui::GetWindowDrawList()->AddRectFilled({ float(x+2), float(y+27) },
{ float(x + width), float(y + 79) }, ImColor(orange));
ImGui::PushStyleColor(ImGuiCol_Text, light_grey);
draw_text(get_title().c_str(), x, y, height - 50);
ImGui::PopStyleColor();
}
}