diff --git a/core/include/mmcore/MegaMolGraph.h b/core/include/mmcore/MegaMolGraph.h index b0f027e003..f3e281937f 100644 --- a/core/include/mmcore/MegaMolGraph.h +++ b/core/include/mmcore/MegaMolGraph.h @@ -83,6 +83,8 @@ class MegaMolGraph { bool SetGraphEntryPoint(std::string moduleName); + bool AddGraphEntryPoint(std::string moduleName); + bool RemoveGraphEntryPoint(std::string moduleName); bool AddFrontendResources(std::vector const& resources); diff --git a/core/src/MegaMolGraph.cpp b/core/src/MegaMolGraph.cpp index 648c841809..f88dffc877 100644 --- a/core/src/MegaMolGraph.cpp +++ b/core/src/MegaMolGraph.cpp @@ -372,6 +372,10 @@ std::vector megamol::core::MegaMolGraph::ListP } bool megamol::core::MegaMolGraph::SetGraphEntryPoint(std::string module) { + return AddGraphEntryPoint(module); +} + +bool megamol::core::MegaMolGraph::AddGraphEntryPoint(std::string module) { auto moduleName = clean(module); // currently, we expect the entry point to be derived from AbstractViewInterface auto module_it = find_module(moduleName); diff --git a/frontend/resources/include/ImagePresentationEntryPoints.h b/frontend/resources/include/ImagePresentationEntryPoints.h index e719335559..8b833cf804 100644 --- a/frontend/resources/include/ImagePresentationEntryPoints.h +++ b/frontend/resources/include/ImagePresentationEntryPoints.h @@ -9,6 +9,7 @@ #include "EntryPoint.h" #include "FrontendResource.h" +#include "ImagePresentationSink.h" #include #include @@ -28,14 +29,21 @@ using EntryPointRenderFunctions = std::tuple< // the ImagePresentation Service manages the entry points - things that are held and "get rendered" by the frontend // using the ImagePresentationEntryPoints resource participants (services) may add/remove entry points to the image presentation struct ImagePresentationEntryPoints { + static std::string const GLFW_Sink_Name; std::function add_entry_point; std::function set_entry_point_priority; std::function remove_entry_point; std::function rename_entry_point; std::function clear_entry_points; + std::function add_sink; + std::function remove_sink; + + std::function bind_sink_entry_point; + std::function unbind_sink_entry_point; + // services may also subscribe to entry point changes to get notified when entry points get added/removed - enum class SubscriptionEvent { Add, Remove, Rename, Clear }; + enum class SubscriptionEvent { Add, Remove, Rename, Clear, AddSink, RemoveSink, BindSink, UnbindSink }; using SubscriberFunction = std::functiongui_hotkeys[HOTKEY_GUI_SHOW_HIDE_GUI] = {"_hotkey_gui_show-hide", megamol::core::view::KeyCode(megamol::core::view::Key::KEY_G, core::view::Modifier::CTRL), false}; + win_collection2.RegisterWindowType(); + win_collection2.AddWindow(TRANSFERFUNCTIONEDITOR_NAME, true); + win_collection2.RegisterWindowType(); + win_collection2.AddWindow(PERFORMANCEMONITOR_NAME); + win_collection2.RegisterWindowType(); + win_collection2.AddWindow(HOTKEYEDITOR_NAME); + win_collection2.RegisterWindowType(); + win_collection2.AddWindow(LOGCONSOLE_NAME); + win_collection2.RegisterWindowType(); + win_collection2.AddWindow( + CONFIGURATOR_NAME, win_collection2.GetWindow(TRANSFERFUNCTIONEDITOR_NAME)); + win_collection2.RegisterWindowType(); + ParameterList::RequestParamWindowCallback_t add_param_list_func = [&](std::string const& name, ImGuiID id) { + win_collection2.AddWindow(name, id, win_collection2.GetWindow(CONFIGURATOR_NAME), + win_collection2.GetWindow(TRANSFERFUNCTIONEDITOR_NAME), add_param_list_func); + }; + win_collection2.AddWindow(PARAMLIST_NAME, GUI_INVALID_ID, + win_collection2.GetWindow(CONFIGURATOR_NAME), + win_collection2.GetWindow(TRANSFERFUNCTIONEDITOR_NAME), add_param_list_func); + this->win_configurator_ptr = this->win_collection.GetWindow(); assert(this->win_configurator_ptr != nullptr); @@ -226,13 +256,13 @@ bool GUIManager::PreDraw(glm::vec2 framebuffer_size, glm::vec2 window_size, doub } // Propagate window specific data - if (auto win_perfmon_ptr = this->win_collection.GetWindow()) { + if (auto win_perfmon_ptr = this->win_collection2.GetWindow(PERFORMANCEMONITOR_NAME)) { win_perfmon_ptr->SetData( this->gui_state.stat_averaged_fps, this->gui_state.stat_averaged_ms, this->gui_state.stat_frame_count); } // Update windows - this->win_collection.Update(); + this->win_collection2.Update(); // Delete window if (this->gui_state.win_delete_hash_id != 0) { @@ -311,7 +341,7 @@ bool GUIManager::PostDraw() { this->picking_buffer.EnableInteraction(glm::vec2(io.DisplaySize.x, io.DisplaySize.y)); graph_ptr->DrawGlobalParameterWidgets( - this->picking_buffer, this->win_collection.GetWindow()); + this->picking_buffer, this->win_collection2.GetWindow(TRANSFERFUNCTIONEDITOR_NAME)); this->picking_buffer.DisableInteraction(); } @@ -549,12 +579,12 @@ void megamol::gui::GUIManager::SetScale(float scale) { } } // Scale all windows - const auto size_func = [&](AbstractWindow& wc) { + const auto size_func = [&](AbstractWindow2& wc) { wc.Config().reset_size *= megamol::gui::gui_scaling.TransitionFactor(); wc.Config().size *= megamol::gui::gui_scaling.TransitionFactor(); wc.Config().reset_pos_size = true; }; - this->win_collection.EnumWindows(size_func); + this->win_collection2.EnumWindows(size_func); } @@ -834,30 +864,33 @@ void GUIManager::draw_menu() { if (ImGui::BeginMenu("Windows")) { ImGui::MenuItem( "Menu", this->gui_hotkeys[HOTKEY_GUI_MENU].keycode.ToString().c_str(), &this->gui_state.menu_visible); - const auto func = [&](AbstractWindow& wc) { - bool registered_window = (wc.Config().hotkey.key != core::view::Key::KEY_UNKNOWN); - if (registered_window) { - ImGui::MenuItem(wc.Name().c_str(), wc.Config().hotkey.ToString().c_str(), &wc.Config().show); + const auto func = [&](WindowType const& wc) { + //bool registered_window = (wc.hotkey.key != core::view::Key::KEY_UNKNOWN); + if (wc.unique) { + ImGui::MenuItem(wc.name.c_str(), wc.hotkey.ToString().c_str(), &wc.show); } else { - // Custom unregistered parameter window - if (ImGui::BeginMenu(wc.Name().c_str())) { - std::string GUI_MENU_label = "Show"; - if (wc.Config().show) - GUI_MENU_label = "Hide"; - if (ImGui::MenuItem(GUI_MENU_label.c_str(), wc.Config().hotkey.ToString().c_str(), nullptr)) { - wc.Config().show = !wc.Config().show; - } - // Enable option to delete custom newly created parameter windows - if (wc.WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS) { - if (ImGui::MenuItem("Delete Window")) { - this->gui_state.win_delete_hash_id = wc.Hash(); - } - } - ImGui::EndMenu(); + if (ImGui::MenuItem("Add", wc.hotkey.ToString().c_str(), nullptr)) { + //win_collection2.AddWindow< } + // Custom unregistered parameter window + //if (ImGui::BeginMenu(wc.name.c_str())) { + // std::string GUI_MENU_label = "Show"; + // if (wc.Config().show) + // GUI_MENU_label = "Hide"; + // if (ImGui::MenuItem(GUI_MENU_label.c_str(), wc.hotkey.ToString().c_str(), nullptr)) { + // wc.Config().show = !wc.Config().show; + // } + // // Enable option to delete custom newly created parameter windows + // /*if (wc.WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS) { + // if (ImGui::MenuItem("Delete Window")) { + // this->gui_state.win_delete_hash_id = wc.Hash(); + // } + // }*/ + // ImGui::EndMenu(); + //} } }; - this->win_collection.EnumWindows(func); + this->win_collection2.EnumRegisteredWindows(func); ImGui::Separator(); @@ -922,8 +955,8 @@ void GUIManager::draw_menu() { ImGui::TextDisabled("Graph Entries:"); for (auto& module_ptr : graph_ptr->Modules()) { if (module_ptr->IsView()) { - if (ImGui::MenuItem(module_ptr->FullName().c_str(), "", module_ptr->IsGraphEntry())) { - if (!module_ptr->IsGraphEntry()) { + if (ImGui::MenuItem(module_ptr->FullName().c_str(), "", module_ptr->HasGLFWSink())) { + if (!module_ptr->HasGLFWSink()) { graph_ptr->AddGraphEntry(module_ptr, graph_ptr->GenerateUniqueGraphEntryName()); } else { graph_ptr->RemoveGraphEntry(module_ptr); @@ -1327,7 +1360,7 @@ void megamol::gui::GUIManager::load_preset_window_docking(ImGuiID global_docking break; } }; - this->win_collection.EnumWindows(func); + this->win_collection.EnumCreatedWindows(func); ImGui::DockBuilderFinish(global_docking_id); #endif @@ -1547,7 +1580,7 @@ std::string GUIManager::extract_fontname(const std::string& imgui_fontname) cons void GUIManager::RegisterHotkeys( megamol::core::view::CommandRegistry& cmdregistry, megamol::core::MegaMolGraph& megamolgraph) { - if (auto win_hkeditor_ptr = this->win_collection.GetWindow()) { + if (auto win_hkeditor_ptr = this->win_collection2.GetWindow(HOTKEYEDITOR_NAME)) { win_hkeditor_ptr->RegisterHotkeys(&cmdregistry, &megamolgraph, &this->win_collection, &this->gui_hotkeys); } } @@ -1557,3 +1590,8 @@ void megamol::gui::GUIManager::setRequestedResources( std::shared_ptr const& resources) { win_collection.setRequestedResources(resources); } + + +void megamol::gui::GUIManager::digestChangedRequestedResources() { + win_collection.digestChangedRequestedResources(); +} diff --git a/frontend/services/gui/src/GUIManager.h b/frontend/services/gui/src/GUIManager.h index 4a4bc85727..5809c25b1d 100644 --- a/frontend/services/gui/src/GUIManager.h +++ b/frontend/services/gui/src/GUIManager.h @@ -24,6 +24,8 @@ #include "windows/LogConsole.h" #include "windows/WindowCollection.h" +#include "windows/WindowCollection2.h" + namespace megamol { namespace gui { @@ -177,6 +179,11 @@ class GUIManager { return this->render_backend.GetImage(); } + + std::vector GetSubscribers() { + return std::vector(); + } + ///////// SET /////////// /** @@ -252,7 +259,7 @@ class GUIManager { void RegisterHotkeys(megamol::core::view::CommandRegistry& cmdregistry, megamol::core::MegaMolGraph& megamol_graph); void SetLuaFunc(megamol::frontend_resources::common_types::lua_func_type* lua_func) { - auto cons = win_collection.GetWindow(); + auto cons = win_collection2.GetWindow("LogConsole"); if (cons) { cons->SetLuaFunc(lua_func); } @@ -318,6 +325,8 @@ class GUIManager { void setRequestedResources(std::shared_ptr const& resources); + void digestChangedRequestedResources(); + /////////////////////////////////////////////////////////////////////// private: @@ -388,7 +397,9 @@ class GUIManager { StateBuffer gui_state; /** GUI element collections. */ - WindowCollection win_collection; + // WindowCollection win_collection; + + WindowCollection2 win_collection2; /** Resource requests from the GUI windows. */ std::vector requested_resources; diff --git a/frontend/services/gui/src/GUI_Service.cpp b/frontend/services/gui/src/GUI_Service.cpp index ca1bdb0b64..55baf9c18b 100644 --- a/frontend/services/gui/src/GUI_Service.cpp +++ b/frontend/services/gui/src/GUI_Service.cpp @@ -133,6 +133,7 @@ void GUI_Service::digestChangedRequestedResources() { this->setShutdown(this->m_gui->GetTriggeredShutdown()); // Check for updates in requested resources -------------------------------- + m_gui->digestChangedRequestedResources(); /// KeyboardEvents = resource index 2 auto maybe_keyboard_events = frontend_resources->getOptional(); @@ -362,6 +363,12 @@ void GUI_Service::setRequestedResources(std::vector resources) megamol::core::utility::log::Log::DefaultLog.WriteInfo( "GUI_Service: error adding graph entry point ... image presentation service rejected GUI Service."); } + { + auto subs = m_gui->GetSubscribers(); + for (auto const& sub : subs) { + image_presentation.subscribe_to_entry_point_changes(sub); + } + } m_exec_lua = const_cast( &frontend_resources->get()); diff --git a/frontend/services/gui/src/graph/Graph.cpp b/frontend/services/gui/src/graph/Graph.cpp index bd10af104f..f547807d07 100644 --- a/frontend/services/gui/src/graph/Graph.cpp +++ b/frontend/services/gui/src/graph/Graph.cpp @@ -211,7 +211,7 @@ ModulePtr_t megamol::gui::Graph::AddModule(const ModuleStockVector_t& stock_modu if (mod_ptr->IsView()) { bool create_new_graph_entry = true; for (auto& module_ptr : this->Modules()) { - if (module_ptr->IsView() && module_ptr->IsGraphEntry()) { + if (module_ptr->IsView() && module_ptr->HasGLFWSink()) { create_new_graph_entry = false; } } @@ -993,7 +993,7 @@ bool Graph::ToggleGraphEntry(bool use_queue) { auto module_graph_entry_iter = this->modules.begin(); // Search for first graph entry and set next view to graph entry (= graph entry point) for (auto module_iter = this->modules.begin(); module_iter != this->modules.end(); module_iter++) { - if ((*module_iter)->IsView() && (*module_iter)->IsGraphEntry()) { + if ((*module_iter)->IsView() && (*module_iter)->HasGLFWSink()) { // Remove all graph entries (*module_iter)->SetGraphEntryName(""); if (this->IsRunning() && use_queue) { @@ -1194,6 +1194,22 @@ bool megamol::gui::Graph::PushSyncQueue(QueueAction action, const QueueData& in_ return false; } } break; + case (QueueAction::BIND_GLFW_SINK): { + if (queue_data.name_id.empty()) { + megamol::core::utility::log::Log::DefaultLog.WriteError( + "[GUI] Graph sync queue action BIND_GLFW_SINK is missing data for 'name_id'. [%s, %s, line %d]\n", + __FILE__, __FUNCTION__, __LINE__); + return false; + } + } break; + case (QueueAction::UNBIND_GLFW_SINK): { + if (queue_data.name_id.empty()) { + megamol::core::utility::log::Log::DefaultLog.WriteError( + "[GUI] Graph sync queue action UNBIND_GLFW_SINK is missing data for 'name_id'. [%s, %s, line %d]\n", + __FILE__, __FUNCTION__, __LINE__); + return false; + } + } break; default: { megamol::core::utility::log::Log::DefaultLog.WriteError( "[GUI] Unknown graph sync queue action. [%s, %s, line %d]\n", __FILE__, __FUNCTION__, __LINE__); @@ -1694,22 +1710,14 @@ void megamol::gui::Graph::Draw(GraphState_t& state) { if (selected_mod_ptr != nullptr) { Graph::QueueData queue_data; if (this->gui_graph_state.interact.module_graphentry_changed == vislib::math::Ternary::TRI_TRUE) { - // Remove all graph entries - for (auto& module_ptr : this->Modules()) { - if (module_ptr->IsView() && module_ptr->IsGraphEntry()) { - module_ptr->SetGraphEntryName(""); - queue_data.name_id = module_ptr->FullName(); - this->PushSyncQueue(Graph::QueueAction::REMOVE_GRAPH_ENTRY, queue_data); - } - } // Add new graph entry queue_data.name_id = selected_mod_ptr->FullName(); selected_mod_ptr->SetGraphEntryName(this->GenerateUniqueGraphEntryName()); - this->PushSyncQueue(Graph::QueueAction::CREATE_GRAPH_ENTRY, queue_data); + this->PushSyncQueue(Graph::BIND_GLFW_SINK, queue_data); } else { queue_data.name_id = selected_mod_ptr->FullName(); selected_mod_ptr->SetGraphEntryName(""); - this->PushSyncQueue(Graph::QueueAction::REMOVE_GRAPH_ENTRY, queue_data); + this->PushSyncQueue(Graph::UNBIND_GLFW_SINK, queue_data); } } this->gui_graph_state.interact.module_graphentry_changed = vislib::math::Ternary::TRI_UNKNOWN; @@ -2128,35 +2136,27 @@ void megamol::gui::Graph::draw_menu(GraphState_t& state) { } } } - // Graph Entry Checkbox + // GLFW Sink Checkbox const float min_text_width = 3.0f * ImGui::GetFrameHeightWithSpacing(); if (selected_mod_ptr == nullptr) { gui_utils::PushReadOnly(); - bool is_graph_entry = false; + bool has_GLFW_sink = false; this->gui_current_graph_entry_name.clear(); - megamol::gui::ButtonWidgets::ToggleButton("Entry", is_graph_entry); + megamol::gui::ButtonWidgets::ToggleButton("Entry", has_GLFW_sink); gui_utils::PopReadOnly(); } else { - bool is_graph_entry = selected_mod_ptr->IsGraphEntry(); - if (megamol::gui::ButtonWidgets::ToggleButton("Entry", is_graph_entry)) { + bool has_GLFW_sink = selected_mod_ptr->HasGLFWSink(); + if (megamol::gui::ButtonWidgets::ToggleButton("Entry", has_GLFW_sink)) { Graph::QueueData queue_data; - if (is_graph_entry) { - // Remove all graph entries - for (auto& module_ptr : this->Modules()) { - if (module_ptr->IsView() && module_ptr->IsGraphEntry()) { - module_ptr->SetGraphEntryName(""); - queue_data.name_id = module_ptr->FullName(); - this->PushSyncQueue(Graph::QueueAction::REMOVE_GRAPH_ENTRY, queue_data); - } - } + if (has_GLFW_sink) { // Add new graph entry selected_mod_ptr->SetGraphEntryName(this->GenerateUniqueGraphEntryName()); queue_data.name_id = selected_mod_ptr->FullName(); - this->PushSyncQueue(Graph::QueueAction::CREATE_GRAPH_ENTRY, queue_data); + this->PushSyncQueue(Graph::BIND_GLFW_SINK, queue_data); } else { selected_mod_ptr->SetGraphEntryName(""); queue_data.name_id = selected_mod_ptr->FullName(); - this->PushSyncQueue(Graph::QueueAction::REMOVE_GRAPH_ENTRY, queue_data); + this->PushSyncQueue(Graph::UNBIND_GLFW_SINK, queue_data); } } } diff --git a/frontend/services/gui/src/graph/Graph.h b/frontend/services/gui/src/graph/Graph.h index be9ba844b2..02021497a7 100644 --- a/frontend/services/gui/src/graph/Graph.h +++ b/frontend/services/gui/src/graph/Graph.h @@ -44,7 +44,9 @@ class Graph { ADD_CALL, DELETE_CALL, CREATE_GRAPH_ENTRY, - REMOVE_GRAPH_ENTRY + REMOVE_GRAPH_ENTRY, + BIND_GLFW_SINK, + UNBIND_GLFW_SINK }; struct QueueData { diff --git a/frontend/services/gui/src/graph/GraphCollection.cpp b/frontend/services/gui/src/graph/GraphCollection.cpp index 12e9c49554..dc407c0996 100644 --- a/frontend/services/gui/src/graph/GraphCollection.cpp +++ b/frontend/services/gui/src/graph/GraphCollection.cpp @@ -366,6 +366,15 @@ bool megamol::gui::GraphCollection::SynchronizeGraphs(megamol::core::MegaMolGrap case (Graph::QueueAction::REMOVE_GRAPH_ENTRY): { (*input_lua_func)("mmRemoveGraphEntryPoint([=[" + data.name_id + "]=])"); } break; + case (Graph::QueueAction::BIND_GLFW_SINK): { + (*input_lua_func)("mmBindSink([=[" + frontend_resources::ImagePresentationEntryPoints::GLFW_Sink_Name + + "]=],[=[" + data.name_id + "]=])"); + } break; + case (Graph::QueueAction::UNBIND_GLFW_SINK): { + (*input_lua_func)("mmUnbindSink([=[" + + frontend_resources::ImagePresentationEntryPoints::GLFW_Sink_Name + "]=],[=[" + + data.name_id + "]=])"); + } break; default: break; } @@ -864,7 +873,7 @@ bool megamol::gui::GraphCollection::SaveProjectToFile( std::string projectstr; std::stringstream confInstances, confModules, confCalls, confParams; for (auto& module_ptr : graph_ptr->Modules()) { - if (module_ptr->IsGraphEntry()) { + if (module_ptr->HasGLFWSink()) { confInstances << "mmCreateView(\"" << module_ptr->GraphEntryName() << "\",\"" << module_ptr->ClassName() << "\",\"" << module_ptr->FullName() << "\")\n"; } else { @@ -1905,7 +1914,7 @@ bool megamol::gui::GraphCollection::change_running_graph(ImGuiID graph_uid) { for (auto& module_ptr : last_running_graph->Modules()) { Graph::QueueData queue_data; queue_data.name_id = module_ptr->FullName(); - if (module_ptr->IsGraphEntry()) { + if (module_ptr->HasGLFWSink()) { running_graph->PushSyncQueue(Graph::QueueAction::REMOVE_GRAPH_ENTRY, queue_data); } if (!running_graph->ModuleExists(module_ptr->FullName())) { @@ -1926,7 +1935,7 @@ bool megamol::gui::GraphCollection::change_running_graph(ImGuiID graph_uid) { queue_data.name_id = module_ptr->FullName(); queue_data.class_name = module_ptr->ClassName(); running_graph->PushSyncQueue(Graph::QueueAction::ADD_MODULE, queue_data); - if (module_ptr->IsGraphEntry()) { + if (module_ptr->HasGLFWSink()) { running_graph->PushSyncQueue(Graph::QueueAction::CREATE_GRAPH_ENTRY, queue_data); } // Set all parameters in GUI graph dirty in order to propagate current values to new core diff --git a/frontend/services/gui/src/graph/Module.cpp b/frontend/services/gui/src/graph/Module.cpp index f8e3990a56..0bcb2d4365 100644 --- a/frontend/services/gui/src/graph/Module.cpp +++ b/frontend/services/gui/src/graph/Module.cpp @@ -472,7 +472,7 @@ void megamol::gui::Module::Draw(megamol::gui::PresentPhase phase, megamol::gui:: ImGui::SetCursorScreenPos(module_center + ImVec2(-item_x_offset, item_y_offset)); if (graph_entry_button) { - bool is_graph_entry = this->IsGraphEntry(); + bool is_graph_entry = this->HasGLFWSink(); if (ImGui::RadioButton("###graph_entry_switch", is_graph_entry)) { if (!is_graph_entry) { state.interact.module_graphentry_changed = vislib::math::Ternary::TRI_TRUE; diff --git a/frontend/services/gui/src/graph/Module.h b/frontend/services/gui/src/graph/Module.h index 4fbe9bf7d0..cf53ea0cd4 100644 --- a/frontend/services/gui/src/graph/Module.h +++ b/frontend/services/gui/src/graph/Module.h @@ -94,7 +94,7 @@ class Module { inline std::string ClassName() const { return this->class_name; } - bool IsGraphEntry() const { + bool HasGLFWSink() const { return (!this->graph_entry_name.empty()); } inline std::string Name() const { diff --git a/frontend/services/gui/src/windows/AbstractWindow.h b/frontend/services/gui/src/windows/AbstractWindow.h index d62125876e..b074981fb3 100644 --- a/frontend/services/gui/src/windows/AbstractWindow.h +++ b/frontend/services/gui/src/windows/AbstractWindow.h @@ -42,7 +42,15 @@ class AbstractWindow { WINDOW_ID_HOTKEYEDITOR = 4, WINDOW_ID_TRANSFER_FUNCTION = 5, WINDOW_ID_CONFIGURATOR = 6, - WINDOW_ID_LOGCONSOLE = 7 + WINDOW_ID_LOGCONSOLE = 7, + WINDOW_ID_RENDERING_ENDPOINT = 8 + }; + + struct WindowType { + bool unique = true; + WindowConfigID id = WINDOW_ID_VOLATILE; + std::string name; + frontend_resources::KeyCode hotkey; }; struct BasicConfig { @@ -67,6 +75,8 @@ class AbstractWindow { frontend_resources = resources; }; + virtual void digestChangedRequestedResources() {} + AbstractWindow(const std::string& name, WindowConfigID window_id) : win_config() , win_hotkeys() diff --git a/frontend/services/gui/src/windows/AbstractWindow2.cpp b/frontend/services/gui/src/windows/AbstractWindow2.cpp new file mode 100644 index 0000000000..243316ac38 --- /dev/null +++ b/frontend/services/gui/src/windows/AbstractWindow2.cpp @@ -0,0 +1,199 @@ +#include "AbstractWindow2.h" + +#include "mmcore/utility/JSONHelper.h" + + +megamol::gui::AbstractWindow2::AbstractWindow2(std::string const& name) : name_(name) {} + + +void megamol::gui::AbstractWindow2::ApplyWindowSizePosition(bool consider_menu) { + assert(ImGui::GetCurrentContext() != nullptr); + + ImGuiIO& io = ImGui::GetIO(); + + // Main menu height + float y_offset = ImGui::GetFrameHeight(); + + ImVec2 win_pos = this->win_config_.position; + ImVec2 win_size = this->win_config_.size; + if (this->win_config_.flags & ImGuiWindowFlags_AlwaysAutoResize) { + win_size = ImGui::GetWindowSize(); + } + + // Fit max window size to viewport + if (win_size.x > io.DisplaySize.x) { + win_size.x = io.DisplaySize.x; + } + if (win_size.y > (io.DisplaySize.y - y_offset)) { + win_size.y = (io.DisplaySize.y - y_offset); + } + + // Snap to viewport + /// ImGui automatically moves windows lying outside viewport + // float win_width = io.DisplaySize.x - (win_pos.x); + // if (win_width < win_size.x) { + // win_pos.x = io.DisplaySize.x - (win_size.x); + //} + // float win_height = io.DisplaySize.y - (win_pos.y); + // if (win_height < win_size.y) { + // win_pos.y = io.DisplaySize.y - (win_size.y); + //} + // if (win_pos.x < 0) { + // win_pos.x = 0.0f; + //} + + // Snap window below menu bar + if (consider_menu && (win_pos.y < y_offset)) { + win_pos.y = y_offset; + } + + this->win_config_.position = win_pos; + // wc.config.reset_position = win_pos; + ImGui::SetWindowPos(win_pos, ImGuiCond_Always); + + this->win_config_.size = win_size; + // wc.config.reset_size = win_size; + ImGui::SetWindowSize(win_size, ImGuiCond_Always); +} + + +void megamol::gui::AbstractWindow2::WindowContextMenu(bool menu_visible, bool& out_collapsing_changed) { + ImGuiIO& io = ImGui::GetIO(); + ImVec2 viewport = io.DisplaySize; + out_collapsing_changed = false; + float y_offset = (menu_visible) ? (ImGui::GetFrameHeight()) : (0.0f); + ImVec2 window_viewport = ImVec2(viewport.x, viewport.y - y_offset); + bool window_maximized = (this->win_config_.size == window_viewport); + bool toggle_window_size = false; // (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)); + + // Context Menu + if (ImGui::BeginPopupContextItem()) { + if (ImGui::MenuItem(((window_maximized) ? ("Minimize") : ("Maximize")))) { + toggle_window_size = true; + } + if (ImGui::MenuItem(((!this->win_config_.collapsed) ? ("Collapse") : ("Expand")), "Double Left Click")) { + this->win_config_.collapsed = !this->win_config_.collapsed; + out_collapsing_changed = true; + } + + if (ImGui::MenuItem("Full Width", nullptr)) { + this->win_config_.size.x = viewport.x; + this->win_config_.reset_pos_size = true; + } + ImGui::Separator(); + +/// DOCKING +#ifdef IMGUI_HAS_DOCK + ImGui::MenuItem("Docking", "Shift + Left-Drag", false, false); + ImGui::Separator(); +#endif + ImGui::MenuItem("Snap", nullptr, false, false); + + if (ImGui::ArrowButton("snap_left", ImGuiDir_Left)) { + this->win_config_.position.x = 0.0f; + this->win_config_.reset_pos_size = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::ArrowButton("snap_up", ImGuiDir_Up)) { + this->win_config_.position.y = 0.0f; + this->win_config_.reset_pos_size = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::ArrowButton("snap_down", ImGuiDir_Down)) { + this->win_config_.position.y = viewport.y - this->win_config_.size.y; + this->win_config_.reset_pos_size = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::ArrowButton("snap_right", ImGuiDir_Right)) { + this->win_config_.position.x = viewport.x - this->win_config_.size.x; + this->win_config_.reset_pos_size = true; + ImGui::CloseCurrentPopup(); + } + ImGui::Separator(); + + if (ImGui::MenuItem("Close", nullptr)) { + this->win_config_.show = false; + } + ImGui::EndPopup(); + } + + // Toggle window size + if (toggle_window_size) { + if (window_maximized) { + // Window is maximized + this->win_config_.size = this->win_config_.reset_size; + this->win_config_.position = this->win_config_.reset_position; + this->win_config_.reset_pos_size = true; + } else { + // Window is minimized + window_viewport = ImVec2(viewport.x, viewport.y - y_offset); + this->win_config_.reset_size = this->win_config_.size; + this->win_config_.reset_position = this->win_config_.position; + this->win_config_.size = window_viewport; + this->win_config_.position = ImVec2(0.0f, y_offset); + this->win_config_.reset_pos_size = true; + } + } +} + + +void megamol::gui::AbstractWindow2::StateFromJSON(const nlohmann::json& in_json) { + for (auto& header_item : in_json.items()) { + if (header_item.key() == GUI_JSON_TAG_WINDOW_CONFIGS) { + for (auto& config_item : header_item.value().items()) { + if (config_item.key() == this->Name()) { + auto config_values = config_item.value(); + + int win_flags = 0; + megamol::core::utility::get_json_value(config_values, {"win_flags"}, &win_flags); + this->win_config_.flags = static_cast(win_flags); + megamol::core::utility::get_json_value(config_values, {"win_show"}, &this->win_config_.show); + std::array hotkey = {0, 0}; + megamol::core::utility::get_json_value( + config_values, {"win_hotkey"}, hotkey.data(), hotkey.size()); + this->win_config_.hotkey = core::view::KeyCode( + static_cast(hotkey[0]), static_cast(hotkey[1])); + std::array position = {0.0f, 0.0f}; + megamol::core::utility::get_json_value( + config_values, {"win_position"}, position.data(), position.size()); + this->win_config_.position = ImVec2(position[0], position[1]); + std::array size = {0.0f, 0.0f}; + megamol::core::utility::get_json_value( + config_values, {"win_size"}, size.data(), size.size()); + this->win_config_.size = ImVec2(size[0], size[1]); + std::array reset_size = {0.0f, 0.0f}; + megamol::core::utility::get_json_value( + config_values, {"win_reset_size"}, reset_size.data(), reset_size.size()); + this->win_config_.reset_size = ImVec2(reset_size[0], reset_size[1]); + std::array reset_position = {0.0f, 0.0f}; + megamol::core::utility::get_json_value( + config_values, {"win_reset_position"}, reset_position.data(), reset_position.size()); + this->win_config_.reset_position = ImVec2(reset_position[0], reset_position[1]); + megamol::core::utility::get_json_value( + config_values, {"win_collapsed"}, &this->win_config_.collapsed); + this->win_config_.reset_pos_size = true; + } + } + } + } +} + + +void megamol::gui::AbstractWindow2::StateToJSON(nlohmann::json& inout_json) const { + inout_json[GUI_JSON_TAG_WINDOW_CONFIGS][this->Name()]["win_show"] = this->win_config_.show; + inout_json[GUI_JSON_TAG_WINDOW_CONFIGS][this->Name()]["win_flags"] = static_cast(this->win_config_.flags); + inout_json[GUI_JSON_TAG_WINDOW_CONFIGS][this->Name()]["win_hotkey"] = { + static_cast(this->win_config_.hotkey.key), this->win_config_.hotkey.mods.toInt()}; + inout_json[GUI_JSON_TAG_WINDOW_CONFIGS][this->Name()]["win_position"] = { + this->win_config_.position.x, this->win_config_.position.y}; + inout_json[GUI_JSON_TAG_WINDOW_CONFIGS][this->Name()]["win_size"] = { + this->win_config_.size.x, this->win_config_.size.y}; + inout_json[GUI_JSON_TAG_WINDOW_CONFIGS][this->Name()]["win_reset_size"] = { + this->win_config_.reset_size.x, this->win_config_.reset_size.y}; + inout_json[GUI_JSON_TAG_WINDOW_CONFIGS][this->Name()]["win_reset_position"] = { + this->win_config_.reset_position.x, this->win_config_.reset_position.y}; + inout_json[GUI_JSON_TAG_WINDOW_CONFIGS][this->Name()]["win_collapsed"] = this->win_config_.collapsed; +} diff --git a/frontend/services/gui/src/windows/AbstractWindow2.h b/frontend/services/gui/src/windows/AbstractWindow2.h new file mode 100644 index 0000000000..21615a7000 --- /dev/null +++ b/frontend/services/gui/src/windows/AbstractWindow2.h @@ -0,0 +1,99 @@ +#pragma once + +#include "KeyboardMouseInput.h" +#include "WindowConfig.h" +#include "gui_utils.h" + +#include "FrontendResourcesMap.h" +#include "nlohmann/json.hpp" + +namespace megamol::gui { + +class ResourceInterface { +public: + virtual std::vector requested_lifetime_resources() const { + return std::vector(); + } + + virtual void setRequestedResources(std::shared_ptr const& resources) { + frontend_resources = resources; + }; + + virtual void digestChangedRequestedResources() {} + +private: + std::shared_ptr frontend_resources; +}; + +class JSONSerializable { +public: + virtual void StateFromJSON(const nlohmann::json& in_json) = 0; + + virtual void StateToJSON(nlohmann::json& inout_json) const = 0; +}; + +struct WindowType { + bool unique = true; + //WindowConfigID id = WINDOW_ID_VOLATILE; + std::string name; + frontend_resources::KeyCode hotkey; + bool show = false; +}; + +class AbstractWindow2 : public ResourceInterface, JSONSerializable { +public: + AbstractWindow2(std::string const& name); + + std::string Name() const { + return name_; + } + + BasicConfig& Config() { + return this->win_config_; + } + + std::string FullWindowTitle() const { + return (this->Name() + " " + this->win_config_.hotkey.ToString()); + } + + megamol::gui::HotkeyMap_t& GetHotkeys() { + return this->win_hotkeys_; + } + + void ApplyWindowSizePosition(bool consider_menu); + + void WindowContextMenu(bool menu_visible, bool& out_collapsing_changed); + + void StateFromJSON(const nlohmann::json& in_json) override; + + void StateToJSON(nlohmann::json& inout_json) const override; + + // -------------------------------------------------------------------- + // IMPLEMENT + + virtual bool Update() { + return true; + } + + virtual bool Draw() { + /*if ((window_id == WINDOW_ID_VOLATILE) && (volatile_draw_callback != nullptr)) { + volatile_draw_callback(this->win_config); + return true; + }*/ + return true; + } + + virtual void PopUps() {} + + virtual void SpecificStateToJSON(nlohmann::json& inout_json) {} + + virtual void SpecificStateFromJSON(const nlohmann::json& in_json){}; + +protected: + BasicConfig win_config_; + megamol::gui::HotkeyMap_t win_hotkeys_; + +private: + std::string name_; +}; +} // namespace megamol::gui diff --git a/frontend/services/gui/src/windows/Configurator.cpp b/frontend/services/gui/src/windows/Configurator.cpp index 8c10b5337b..c58df4bf91 100644 --- a/frontend/services/gui/src/windows/Configurator.cpp +++ b/frontend/services/gui/src/windows/Configurator.cpp @@ -14,7 +14,7 @@ using namespace megamol::gui; megamol::gui::Configurator::Configurator( const std::string& window_name, std::shared_ptr win_tfe_ptr) - : AbstractWindow(window_name, AbstractWindow::WINDOW_ID_CONFIGURATOR) + : AbstractWindow2(window_name) , graph_state() , graph_collection() , win_tfeditor_ptr(win_tfe_ptr) @@ -35,13 +35,13 @@ megamol::gui::Configurator::Configurator( assert(this->win_tfeditor_ptr != nullptr); // init hotkeys - this->win_hotkeys[HOTKEY_CONFIGURATOR_MODULE_SEARCH] = {"_hotkey_gui_configurator_module_search", + this->win_hotkeys_[HOTKEY_CONFIGURATOR_MODULE_SEARCH] = {"_hotkey_gui_configurator_module_search", core::view::KeyCode(core::view::Key::KEY_M, (core::view::Modifier::CTRL | core::view::Modifier::SHIFT)), false}; - this->win_hotkeys[HOTKEY_CONFIGURATOR_PARAMETER_SEARCH] = {"_hotkey_gui_configurator_param_search", + this->win_hotkeys_[HOTKEY_CONFIGURATOR_PARAMETER_SEARCH] = {"_hotkey_gui_configurator_param_search", core::view::KeyCode(core::view::Key::KEY_P, (core::view::Modifier::CTRL | core::view::Modifier::SHIFT)), false}; - this->win_hotkeys[HOTKEY_CONFIGURATOR_DELETE_GRAPH_ITEM] = { + this->win_hotkeys_[HOTKEY_CONFIGURATOR_DELETE_GRAPH_ITEM] = { "_hotkey_gui_configurator_delete_graph_entry", core::view::KeyCode(core::view::Key::KEY_DELETE), false}; - this->win_hotkeys[HOTKEY_CONFIGURATOR_SAVE_PROJECT] = {"_hotkey_gui_configurator_save_project", + this->win_hotkeys_[HOTKEY_CONFIGURATOR_SAVE_PROJECT] = {"_hotkey_gui_configurator_save_project", megamol::core::view::KeyCode(core::view::Key::KEY_S, core::view::Modifier::CTRL | core::view::Modifier::SHIFT), false}; @@ -56,11 +56,10 @@ megamol::gui::Configurator::Configurator( this->graph_state.new_running_graph_uid = GUI_INVALID_ID; // Configure CONFIGURATOR Window - this->win_config.size = ImVec2(750.0f * megamol::gui::gui_scaling.Get(), 500.0f * megamol::gui::gui_scaling.Get()); - this->win_config.reset_size = this->win_config.size; - this->win_config.flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNavInputs; - this->win_config.hotkey = - megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F11, core::view::Modifier::NONE); + this->win_config_.size = ImVec2(750.0f * megamol::gui::gui_scaling.Get(), 500.0f * megamol::gui::gui_scaling.Get()); + this->win_config_.reset_size = this->win_config_.size; + this->win_config_.flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNavInputs; + this->win_config_.hotkey = GetTypeInfo().hotkey; } @@ -106,7 +105,7 @@ bool megamol::gui::Configurator::Draw() { } // Update state ------------------------------------------------------- - this->graph_state.hotkeys = this->win_hotkeys; + this->graph_state.hotkeys = this->win_hotkeys_; // Process hotkeys /// HOTKEY_CONFIGURATOR_SAVE_PROJECT @@ -155,7 +154,7 @@ bool megamol::gui::Configurator::Draw() { // Only reset 'externally' processed hotkeys this->graph_state.hotkeys[HOTKEY_CONFIGURATOR_PARAMETER_SEARCH].is_pressed = false; this->graph_state.hotkeys[HOTKEY_CONFIGURATOR_DELETE_GRAPH_ITEM].is_pressed = false; - this->win_hotkeys = this->graph_state.hotkeys; + this->win_hotkeys_ = this->graph_state.hotkeys; return true; } @@ -216,16 +215,16 @@ void megamol::gui::Configurator::PopUps() { ImGui::OpenPopup(pop_up_id.c_str(), ImGuiPopupFlags_None); this->search_widget.SetSearchFocus(); - float diff_width = (this->win_config.position.x + this->win_config.size.x - this->module_list_popup_pos.x); - float diff_height = (this->win_config.position.y + this->win_config.size.y - this->module_list_popup_pos.y); + float diff_width = (this->win_config_.position.x + this->win_config_.size.x - this->module_list_popup_pos.x); + float diff_height = (this->win_config_.position.y + this->win_config_.size.y - this->module_list_popup_pos.y); if (diff_width < popup_width) { this->module_list_popup_pos.x -= ((popup_width - diff_width) + offset_x); } - this->module_list_popup_pos.x = std::max(this->module_list_popup_pos.x, this->win_config.position.x); + this->module_list_popup_pos.x = std::max(this->module_list_popup_pos.x, this->win_config_.position.x); if (diff_height < popup_height) { this->module_list_popup_pos.y -= ((popup_height - diff_height) + offset_y); } - this->module_list_popup_pos.y = std::max(this->module_list_popup_pos.y, this->win_config.position.y); + this->module_list_popup_pos.y = std::max(this->module_list_popup_pos.y, this->win_config_.position.y); ImGui::SetNextWindowPos(this->module_list_popup_pos); ImGui::SetNextWindowSize(ImVec2(10.0f, 10.0f)); } diff --git a/frontend/services/gui/src/windows/Configurator.h b/frontend/services/gui/src/windows/Configurator.h index 3176cb404e..3d98f4f0cf 100644 --- a/frontend/services/gui/src/windows/Configurator.h +++ b/frontend/services/gui/src/windows/Configurator.h @@ -17,6 +17,8 @@ #include "widgets/SplitterWidget.h" #include "widgets/StringSearchWidget.h" +#include "AbstractWindow2.h" + namespace megamol { namespace gui { @@ -25,8 +27,16 @@ namespace gui { /* ************************************************************************ * The graph configurator GUI window */ -class Configurator : public AbstractWindow { +class Configurator : public AbstractWindow2 { public: + static WindowType GetTypeInfo() { + WindowType wt; + wt.unique = true; + wt.name = "Configurator"; + wt.hotkey = megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F11, core::view::Modifier::NONE); + return wt; + } + explicit Configurator(const std::string& window_name, std::shared_ptr win_tfe_ptr); ~Configurator() = default; diff --git a/frontend/services/gui/src/windows/HotkeyEditor.cpp b/frontend/services/gui/src/windows/HotkeyEditor.cpp index 9f970a0470..d7b4badac2 100644 --- a/frontend/services/gui/src/windows/HotkeyEditor.cpp +++ b/frontend/services/gui/src/windows/HotkeyEditor.cpp @@ -12,7 +12,7 @@ using namespace megamol::gui; megamol::gui::HotkeyEditor::HotkeyEditor(const std::string& window_name) - : AbstractWindow(window_name, AbstractWindow::WINDOW_ID_HOTKEYEDITOR) + : AbstractWindow2(window_name) , search_widget() , tooltip_widget() , pending_hotkey_assignment(0) @@ -26,11 +26,10 @@ megamol::gui::HotkeyEditor::HotkeyEditor(const std::string& window_name) , parent_gui_window_hotkey_lambda() { // Configure HOTKEY EDITOR Window - this->win_config.size = ImVec2(0.0f * megamol::gui::gui_scaling.Get(), 0.0f * megamol::gui::gui_scaling.Get()); - this->win_config.reset_size = this->win_config.size; - this->win_config.flags = ImGuiWindowFlags_NoNavInputs; - this->win_config.hotkey = - megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F6, core::view::Modifier::NONE); + this->win_config_.size = ImVec2(0.0f * megamol::gui::gui_scaling.Get(), 0.0f * megamol::gui::gui_scaling.Get()); + this->win_config_.reset_size = this->win_config_.size; + this->win_config_.flags = ImGuiWindowFlags_NoNavInputs; + this->win_config_.hotkey = GetTypeInfo().hotkey; } @@ -81,7 +80,7 @@ void megamol::gui::HotkeyEditor::RegisterHotkeys(megamol::core::view::CommandReg wc.Config().show = !wc.Config().show; } }; - this->window_collection_ptr->EnumWindows(wf); + this->window_collection_ptr->EnumCreatedWindows(wf); }; this->parent_gui_window_hotkey_lambda = [&](const frontend_resources::Command* self) { @@ -92,7 +91,7 @@ void megamol::gui::HotkeyEditor::RegisterHotkeys(megamol::core::view::CommandReg } } }; - this->window_collection_ptr->EnumWindows(wf); + this->window_collection_ptr->EnumCreatedWindows(wf); }; @@ -129,7 +128,7 @@ void megamol::gui::HotkeyEditor::RegisterHotkeys(megamol::core::view::CommandReg cmdregistry->add_command(hkcmd); } }; - this->window_collection_ptr->EnumWindows(windows_func); + this->window_collection_ptr->EnumCreatedWindows(windows_func); } diff --git a/frontend/services/gui/src/windows/HotkeyEditor.h b/frontend/services/gui/src/windows/HotkeyEditor.h index db6f793f99..d1c1564dee 100644 --- a/frontend/services/gui/src/windows/HotkeyEditor.h +++ b/frontend/services/gui/src/windows/HotkeyEditor.h @@ -17,12 +17,22 @@ #include "widgets/HoverToolTip.h" #include "widgets/StringSearchWidget.h" +#include "AbstractWindow2.h" + namespace megamol { namespace gui { -class HotkeyEditor : public AbstractWindow { +class HotkeyEditor : public AbstractWindow2 { public: + static WindowType GetTypeInfo() { + WindowType wt; + wt.unique = true; + wt.name = "HotkeyEditor"; + wt.hotkey = megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F6, core::view::Modifier::NONE); + return wt; + } + explicit HotkeyEditor(const std::string& window_name); ~HotkeyEditor(); diff --git a/frontend/services/gui/src/windows/LogConsole.cpp b/frontend/services/gui/src/windows/LogConsole.cpp index 69ed21da38..d964f5a046 100644 --- a/frontend/services/gui/src/windows/LogConsole.cpp +++ b/frontend/services/gui/src/windows/LogConsole.cpp @@ -204,7 +204,7 @@ int megamol::gui::LogBuffer::sync() { // ---------------------------------------------------------------------------- megamol::gui::LogConsole::LogConsole(const std::string& window_name) - : AbstractWindow(window_name, AbstractWindow::WINDOW_ID_LOGCONSOLE) + : AbstractWindow2(window_name) , echo_log_buffer() , echo_log_stream(&this->echo_log_buffer) , log_msg_count(0) @@ -226,12 +226,13 @@ megamol::gui::LogConsole::LogConsole(const std::string& window_name) sink_idx_ = megamol::core::utility::log::Log::DefaultLog.AddEchoTarget(sink); // Configure CONSOLE Window - this->win_config.size = ImVec2(500.0f * megamol::gui::gui_scaling.Get(), 50.0f * megamol::gui::gui_scaling.Get()); - this->win_config.reset_size = this->win_config.size; - this->win_config.flags = + this->win_config_.size = ImVec2(500.0f * megamol::gui::gui_scaling.Get(), 50.0f * megamol::gui::gui_scaling.Get()); + this->win_config_.reset_size = this->win_config_.size; + this->win_config_.flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoNavInputs; - this->win_config.hotkey = - megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F9, core::view::Modifier::NONE); + /*this->win_config_.hotkey = + megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F9, core::view::Modifier::NONE);*/ + this->win_config_.hotkey = GetTypeInfo().hotkey; // Initialise this->input_shared_data = std::make_shared(); @@ -261,7 +262,7 @@ bool megamol::gui::LogConsole::Update() { if (this->win_log_force_open) { if (entry.level == megamol::core::utility::log::Log::log_level::warn || entry.level == megamol::core::utility::log::Log::log_level::error) { - this->win_config.show = true; + this->win_config_.show = true; } } } diff --git a/frontend/services/gui/src/windows/LogConsole.h b/frontend/services/gui/src/windows/LogConsole.h index 40f1c27942..cb52b2007d 100644 --- a/frontend/services/gui/src/windows/LogConsole.h +++ b/frontend/services/gui/src/windows/LogConsole.h @@ -15,6 +15,8 @@ #include "widgets/HoverToolTip.h" #include "widgets/PopUps.h" +#include "AbstractWindow2.h" + namespace megamol { namespace gui { @@ -57,7 +59,7 @@ class LogBuffer : public std::stringbuf { /* ************************************************************************ * The content of the log console GUI window */ -class LogConsole : public AbstractWindow { +class LogConsole : public AbstractWindow2 { public: using lua_func_type = megamol::frontend_resources::common_types::lua_func_type; @@ -71,6 +73,14 @@ class LogConsole : public AbstractWindow { std::string param_hint; }; + static WindowType GetTypeInfo() { + WindowType wt; + wt.unique = true; + wt.name = "LogConsole"; + wt.hotkey = megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F9, core::view::Modifier::NONE); + return wt; + } + explicit LogConsole(const std::string& window_name); ~LogConsole(); diff --git a/frontend/services/gui/src/windows/ParameterList.cpp b/frontend/services/gui/src/windows/ParameterList.cpp index 2eb0e8c494..dcab4c7b2b 100644 --- a/frontend/services/gui/src/windows/ParameterList.cpp +++ b/frontend/services/gui/src/windows/ParameterList.cpp @@ -14,10 +14,10 @@ using namespace megamol::gui; -ParameterList::ParameterList(const std::string& window_name, AbstractWindow::WindowConfigID win_id, - ImGuiID initial_module_uid, std::shared_ptr win_configurator, - std::shared_ptr win_tfeditor, const RequestParamWindowCallback_t& add_parameter_window) - : AbstractWindow(window_name, win_id) +ParameterList::ParameterList(const std::string& window_name, ImGuiID initial_module_uid, + std::shared_ptr win_configurator, std::shared_ptr win_tfeditor, + const RequestParamWindowCallback_t& add_parameter_window) + : AbstractWindow2(window_name) , win_configurator_ptr(win_configurator) , win_tfeditor_ptr(win_tfeditor) , request_new_parameter_window_func(add_parameter_window) @@ -26,18 +26,18 @@ ParameterList::ParameterList(const std::string& window_name, AbstractWindow::Win , search_widget() , tooltip() { - assert((this->WindowID() == AbstractWindow::WINDOW_ID_MAIN_PARAMETERS) || - (this->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS)); + /*assert((this->WindowID() == AbstractWindow::WINDOW_ID_MAIN_PARAMETERS) || + (this->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS));*/ assert(this->win_configurator_ptr != nullptr); assert(this->win_tfeditor_ptr != nullptr); // Configure PARAMETER LIST Window - this->win_config.show = true; - this->win_config.size = ImVec2(400.0f * megamol::gui::gui_scaling.Get(), 500.0f * megamol::gui::gui_scaling.Get()); - this->win_config.reset_size = this->win_config.size; - this->win_config.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNavInputs; + this->win_config_.show = true; + this->win_config_.size = ImVec2(400.0f * megamol::gui::gui_scaling.Get(), 500.0f * megamol::gui::gui_scaling.Get()); + this->win_config_.reset_size = this->win_config_.size; + this->win_config_.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNavInputs; - if (this->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS) { + /*if (this->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS) { if (initial_module_uid != GUI_INVALID_ID) { this->win_modules_list.emplace_back(initial_module_uid); } @@ -46,7 +46,10 @@ ParameterList::ParameterList(const std::string& window_name, AbstractWindow::Win megamol::core::view::KeyCode(megamol::core::view::Key::KEY_P, core::view::Modifier::CTRL), false}; this->win_config.hotkey = megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F10, core::view::Modifier::NONE); - } + }*/ + this->win_hotkeys_[HOTKEY_GUI_PARAMETER_SEARCH] = {"_hotkey_gui_parameterlist_param_search", + megamol::core::view::KeyCode(megamol::core::view::Key::KEY_P, core::view::Modifier::CTRL), false}; + this->win_config_.hotkey = GetTypeInfo().hotkey; } @@ -86,12 +89,12 @@ bool ParameterList::Draw() { this->tooltip.ToolTip(param_help); // Parameter substring name filtering (only for main parameter view) - if (this->WindowID() == AbstractWindow::WINDOW_ID_MAIN_PARAMETERS) { - if (this->win_hotkeys[HOTKEY_GUI_PARAMETER_SEARCH].is_pressed) { + /*if (this->WindowID() == AbstractWindow::WINDOW_ID_MAIN_PARAMETERS)*/ { + if (this->win_hotkeys_[HOTKEY_GUI_PARAMETER_SEARCH].is_pressed) { this->search_widget.SetSearchFocus(); - this->win_hotkeys[HOTKEY_GUI_PARAMETER_SEARCH].is_pressed = false; + this->win_hotkeys_[HOTKEY_GUI_PARAMETER_SEARCH].is_pressed = false; } - std::string help_test = "[" + this->win_hotkeys[HOTKEY_GUI_PARAMETER_SEARCH].keycode.ToString() + + std::string help_test = "[" + this->win_hotkeys_[HOTKEY_GUI_PARAMETER_SEARCH].keycode.ToString() + "] Set keyboard focus to search input field.\n" "Case insensitive substring search in module and parameter names."; this->search_widget.Widget("guiwindow_parameter_search", help_test); @@ -112,13 +115,13 @@ bool ParameterList::Draw() { bool skip = false; std::string module_label = module_ptr->FullName(); // Consider always all modules for main parameter window - if (this->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS) { - // Check if module should be considered. - if (std::find(this->win_modules_list.begin(), this->win_modules_list.end(), module_ptr->UID()) == - this->win_modules_list.end()) { - skip = true; - } - } + //if (this->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS) { + // // Check if module should be considered. + // if (std::find(this->win_modules_list.begin(), this->win_modules_list.end(), module_ptr->UID()) == + // this->win_modules_list.end()) { + // skip = true; + // } + //} if (!skip && !this->win_extended_mode) { skip = !module_ptr->ParametersVisible(); } @@ -163,21 +166,20 @@ bool ParameterList::Draw() { if (ImGui::MenuItem("Copy to new Window")) { std::srand(std::time(nullptr)); std::string window_name = "Parameters###parameters_" + std::to_string(std::rand()); - this->request_new_parameter_window_func( - window_name, AbstractWindow::WINDOW_ID_PARAMETERS, module_ptr->UID()); + this->request_new_parameter_window_func(window_name, module_ptr->UID()); } // Deleting module's parameters is not available in main parameter window. - if (this->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS) { - if (ImGui::MenuItem("Delete from List")) { - auto find_iter = std::find( - this->win_modules_list.begin(), this->win_modules_list.end(), module_ptr->UID()); - // Break if module name is not contained in list - if (find_iter != this->win_modules_list.end()) { - this->win_modules_list.erase(find_iter); - } - } - } + //if (this->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS) { + // if (ImGui::MenuItem("Delete from List")) { + // auto find_iter = std::find( + // this->win_modules_list.begin(), this->win_modules_list.end(), module_ptr->UID()); + // // Break if module name is not contained in list + // if (find_iter != this->win_modules_list.end()) { + // this->win_modules_list.erase(find_iter); + // } + // } + //} ImGui::EndPopup(); } diff --git a/frontend/services/gui/src/windows/ParameterList.h b/frontend/services/gui/src/windows/ParameterList.h index c8f3e379e9..34626d8622 100644 --- a/frontend/services/gui/src/windows/ParameterList.h +++ b/frontend/services/gui/src/windows/ParameterList.h @@ -15,6 +15,8 @@ #include "windows/Configurator.h" #include "windows/TransferFunctionEditor.h" +#include "AbstractWindow2.h" + namespace megamol { namespace gui { @@ -23,12 +25,19 @@ namespace gui { /* ************************************************************************ * The parameter list GUI window */ -class ParameterList : public AbstractWindow { +class ParameterList : public AbstractWindow2 { public: - typedef std::function - RequestParamWindowCallback_t; + static WindowType GetTypeInfo() { + WindowType wt; + wt.unique = true; + wt.name = "ParameterList"; + wt.hotkey = megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F10, core::view::Modifier::NONE); + return wt; + } + + typedef std::function RequestParamWindowCallback_t; - ParameterList(const std::string& window_name, AbstractWindow::WindowConfigID win_id, ImGuiID initial_module_uid, + ParameterList(const std::string& window_name, ImGuiID initial_module_uid, std::shared_ptr win_configurator, std::shared_ptr win_tfeditor, const RequestParamWindowCallback_t& add_parameter_window); ~ParameterList() = default; diff --git a/frontend/services/gui/src/windows/PerformanceMonitor.cpp b/frontend/services/gui/src/windows/PerformanceMonitor.cpp index 3e7da7fcfa..6a2ecc1d3e 100644 --- a/frontend/services/gui/src/windows/PerformanceMonitor.cpp +++ b/frontend/services/gui/src/windows/PerformanceMonitor.cpp @@ -16,7 +16,7 @@ using namespace megamol::gui; PerformanceMonitor::PerformanceMonitor(const std::string& window_name) - : AbstractWindow(window_name, AbstractWindow::WINDOW_ID_PERFORMANCE) + : AbstractWindow2(window_name) , win_show_options(false) , win_buffer_size(20) , win_refresh_rate(2.0f) @@ -31,10 +31,9 @@ PerformanceMonitor::PerformanceMonitor(const std::string& window_name) , averaged_ms(0.0f) { // Configure FPS/MS Window - this->win_config.flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | + this->win_config_.flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoNavInputs; - this->win_config.hotkey = - megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F7, core::view::Modifier::NONE); + this->win_config_.hotkey = GetTypeInfo().hotkey; } diff --git a/frontend/services/gui/src/windows/PerformanceMonitor.h b/frontend/services/gui/src/windows/PerformanceMonitor.h index 03634a77cf..19bd09afcf 100644 --- a/frontend/services/gui/src/windows/PerformanceMonitor.h +++ b/frontend/services/gui/src/windows/PerformanceMonitor.h @@ -13,6 +13,8 @@ #include "AbstractWindow.h" #include "widgets/HoverToolTip.h" +#include "AbstractWindow2.h" + namespace megamol { namespace gui { @@ -20,8 +22,16 @@ namespace gui { /* ************************************************************************ * The performance monitor GUI window */ -class PerformanceMonitor : public AbstractWindow { +class PerformanceMonitor : public AbstractWindow2 { public: + struct WindowType GetTypeInfo() { + WindowType wt; + wt.unique = true; + wt.name = "PerformanceMonitor"; + wt.hotkey = megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F7, core::view::Modifier::NONE); + return wt; + } + enum TimingMode { TIMINGMODE_FPS, TIMINGMODE_MS }; explicit PerformanceMonitor(const std::string& window_name); diff --git a/frontend/services/gui/src/windows/RenderingEndPoint.cpp b/frontend/services/gui/src/windows/RenderingEndPoint.cpp new file mode 100644 index 0000000000..df3272dce6 --- /dev/null +++ b/frontend/services/gui/src/windows/RenderingEndPoint.cpp @@ -0,0 +1,123 @@ +#include "RenderingEndPoint.h" + +#include "mmcore/utility/log/Log.h" + + +megamol::gui::RenderingEndPoint::RenderingEndPoint(std::string const& window_name) + : AbstractWindow(window_name, AbstractWindow::WINDOW_ID_RENDERING_ENDPOINT) { + sink_.name = window_name; + sink_.present_images = std::bind(&RenderingEndPoint::PresentImageCB, this, std::placeholders::_1); +} + + +//void megamol::gui::RenderingEndPoint::SetTexture(GLuint texture, uint32_t x, uint32_t y) { +// tex_ = reinterpret_cast(static_cast(texture)); +// size_ = ImVec2(x, y); +//} + + +void megamol::gui::RenderingEndPoint::digestChangedRequestedResources() { + auto mouse_events_resource_ptr = frontend_resources->getOptional(); + if (mouse_events_resource_ptr.has_value()) { + frontend_resources::MouseEvents const& me = mouse_events_resource_ptr.value(); + const_cast(me).position_events.insert( + const_cast(me).position_events.end(), position_events_.begin(), + position_events_.end()); + const_cast(me).buttons_events.insert( + const_cast(me).buttons_events.end(), buttons_events_.begin(), + buttons_events_.end()); + } + position_events_.clear(); + buttons_events_.clear(); +} + + +bool megamol::gui::RenderingEndPoint::Draw() { + static const char* current_item = nullptr; + megamol::frontend::ImagePresentation_Service::EntryPointRenderFunctions entry_point; + /*if (ImGui::BeginMainMenuBar()) { + + + ImGui::EndMainMenuBar(); + }*/ + + auto& img_pres_ep_resource_ptr = frontend_resources->get(); + auto mouse_events_resource_ptr = frontend_resources->getOptional(); + + bool isSelected = false; + if (ImGui::BeginCombo("Views", current_item)) { + for (auto const& item : entry_points_) { + if (ImGui::Selectable(item.first.c_str(), &isSelected)) { + if (current_item != nullptr) { + img_pres_ep_resource_ptr.unbind_sink_entry_point(this->Name(), item.first); + } + current_item = item.first.c_str(); + entry_point = item.second; + img_pres_ep_resource_ptr.bind_sink_entry_point(this->Name(), item.first); + } + } + ImGui::EndCombo(); + } + + /*ImGui::Text("RenderEndPoint"); + ImGui::Spacing();*/ + + //entry_point + if (current_item != nullptr) { + if (mouse_events_resource_ptr.has_value() && ImGui::IsWindowHovered()) { + ImVec2 mousePositionAbsolute = ImGui::GetMousePos(); + ImVec2 screenPositionAbsolute = ImGui::GetItemRectMin(); + ImVec2 mousePositionRelative = ImVec2( + mousePositionAbsolute.x - screenPositionAbsolute.x, mousePositionAbsolute.y - screenPositionAbsolute.y); + /*frontend_resources::MouseEvents const& me = mouse_events_resource_ptr.value(); + const_cast(me).position_events.emplace_back( + std::make_tuple(mousePositionRelative.x, mousePositionRelative.y));*/ + position_events_.emplace_back(std::make_tuple(mousePositionRelative.x, mousePositionRelative.y)); + + if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) { + if (ImGui::IsKeyDown(ImGuiKey_ModAlt)) { + frontend_resources::MouseButton btn = frontend_resources::MouseButton::BUTTON_LEFT; + frontend_resources::MouseButtonAction btnaction = frontend_resources::MouseButtonAction::PRESS; + + + frontend_resources::Modifiers btnmods; + btnmods |= frontend_resources::Modifier::ALT; + + /*const_cast(me).buttons_events.emplace_back( + std::make_tuple(btn, btnaction, btnmods));*/ + buttons_events_.emplace_back(std::make_tuple(btn, btnaction, btnmods)); + } + } + + /*core::utility::log::Log::DefaultLog.WriteInfo( + "Window coord %f %f", mousePositionRelative.x, mousePositionRelative.y);*/ + } + for (auto& image : images_) { + ImGui::Image(image.referenced_image_handle, ImVec2{(float)image.size.width, (float)image.size.height}, + ImVec2(0, 1), ImVec2(1, 0)); + } + + //auto ep = img_pres_ep_resource_ptr.get_entry_point(current_item); + //if (ep.has_value()) { + // frontend_resources::EntryPoint& ep_v = ep.value(); + + // /*ep_v.entry_point_data->update(); + + // ep_v.execute(ep_v.modulePtr, ep_v.entry_point_resources, ep_v.execution_result_image);*/ + + // ImGui::Image(ep_v.execution_result_image.referenced_image_handle, + // ImVec2{(float)ep_v.execution_result_image.size.width, (float)ep_v.execution_result_image.size.height}, + // ImVec2(0, 1), ImVec2(1, 0)); + // //ImGui::Image(tex_, size_, ImVec2(0, 1), ImVec2(1, 0)); + //} + } + /*if (ImGui::Begin("RenderingEndPoint")) { + ImGui::Text("RenderEndPoint"); + ImGui::Spacing(); + + ImGui::Image(tex_, size_, ImVec2(0, 1), ImVec2(1, 0)); + } + ImGui::End();*/ + + return true; +} diff --git a/frontend/services/gui/src/windows/RenderingEndPoint.h b/frontend/services/gui/src/windows/RenderingEndPoint.h new file mode 100644 index 0000000000..b4364a9197 --- /dev/null +++ b/frontend/services/gui/src/windows/RenderingEndPoint.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include + +#include + +#include "AbstractWindow.h" + +#include "ImagePresentationEntryPoints.h" +#include "ImagePresentation_Service.hpp" + +#include "KeyboardMouse_Events.h" + +namespace megamol::gui { +class RenderingEndPoint : public AbstractWindow { +public: + std::vector requested_lifetime_resources() const override { + auto res = AbstractWindow::requested_lifetime_resources(); + res.push_back("ImagePresentationEntryPoints"); + res.push_back("optional"); + return res; + } + + void setRequestedResources(std::shared_ptr const& resources) override { + AbstractWindow::setRequestedResources(resources); + auto& img_pres_ep_resource_ptr = frontend_resources->get(); + + auto sub_func = [&](frontend_resources::ImagePresentationEntryPoints::SubscriptionEvent const& event, + std::vector const& args) -> void { + switch (event) { + case frontend_resources::ImagePresentationEntryPoints::SubscriptionEvent::Add: { + entry_points_.insert(std::make_pair(std::any_cast(args[0]), + std::any_cast(args[1]))); + } break; + case frontend_resources::ImagePresentationEntryPoints::SubscriptionEvent::Remove: { + entry_points_.erase(std::any_cast(args[0])); + } break; + case frontend_resources::ImagePresentationEntryPoints::SubscriptionEvent::Rename: { + auto func = entry_points_[std::any_cast(args[0])]; + entry_points_.erase(std::any_cast(args[0])); + entry_points_.insert(std::make_pair(std::any_cast(args[1]), func)); + } break; + case frontend_resources::ImagePresentationEntryPoints::SubscriptionEvent::Clear: + default: + break; + } + }; + + img_pres_ep_resource_ptr.subscribe_to_entry_point_changes(sub_func); + img_pres_ep_resource_ptr.add_sink(sink_); + } + + void digestChangedRequestedResources() override; + + explicit RenderingEndPoint(const std::string& window_name); + + //virtual ~RenderingEndPoint() { + // auto& img_pres_ep_resource_ptr = frontend_resources->get(); + // img_pres_ep_resource_ptr.remove_sink(sink_.name); + //} + + //void SetTexture(GLuint texture, uint32_t x, uint32_t y); + + bool Draw() override; + + void PresentImageCB(std::vector const& images) { + images_ = images; + } + +private: + /*ImTextureID tex_; + ImVec2 size_;*/ + std::map entry_points_; + std::vector images_; + frontend_resources::ImagePresentationSink sink_; + + std::vector> + buttons_events_; + std::vector> position_events_; +}; +} // namespace megamol::gui diff --git a/frontend/services/gui/src/windows/TransferFunctionEditor.cpp b/frontend/services/gui/src/windows/TransferFunctionEditor.cpp index 58ed91f1e3..6326707a2f 100644 --- a/frontend/services/gui/src/windows/TransferFunctionEditor.cpp +++ b/frontend/services/gui/src/windows/TransferFunctionEditor.cpp @@ -165,7 +165,7 @@ std::array, 21> PRESETS = { // ---------------------------------------------------------------------------- TransferFunctionEditor::TransferFunctionEditor(const std::string& window_name, bool windowed) - : AbstractWindow(window_name, AbstractWindow::WINDOW_ID_TRANSFER_FUNCTION) + : AbstractWindow2(window_name) , windowed_mode(windowed) , connected_parameter_ptr(nullptr) , nodes() @@ -206,9 +206,8 @@ TransferFunctionEditor::TransferFunctionEditor(const std::string& window_name, b RampAdapter(this->nodes, this->texture_size); // Configure TRANSFER FUNCTION Window - this->win_config.flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNavInputs; - this->win_config.hotkey = - megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F8, core::view::Modifier::NONE); + this->win_config_.flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNavInputs; + this->win_config_.hotkey = GetTypeInfo().hotkey; } @@ -313,11 +312,11 @@ bool TransferFunctionEditor::Update() { // Change window flags depending on current view of transfer function editor if (this->IsMinimized()) { - this->win_config.flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | + this->win_config_.flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoNavInputs; } else { - this->win_config.flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNavInputs; + this->win_config_.flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoNavInputs; } this->win_view_minimized = this->IsMinimized(); this->win_view_vertical = this->IsVertical(); diff --git a/frontend/services/gui/src/windows/TransferFunctionEditor.h b/frontend/services/gui/src/windows/TransferFunctionEditor.h index d917313881..4458869c6f 100644 --- a/frontend/services/gui/src/windows/TransferFunctionEditor.h +++ b/frontend/services/gui/src/windows/TransferFunctionEditor.h @@ -15,6 +15,8 @@ #include "widgets/HoverToolTip.h" #include "widgets/ImageWidget.h" +#include "AbstractWindow2.h" + using namespace megamol::core::param; @@ -28,8 +30,16 @@ class Parameter; /** ************************************************************************ * 1D Transfer Function Editor GUI window */ -class TransferFunctionEditor : public AbstractWindow { +class TransferFunctionEditor : public AbstractWindow2 { public: + static WindowType GetTypeInfo() { + WindowType wt; + wt.unique = true; + wt.name = "TransferFunctionEditor"; + wt.hotkey = megamol::core::view::KeyCode(megamol::core::view::Key::KEY_F8, core::view::Modifier::NONE); + return wt; + } + TransferFunctionEditor(const std::string& window_name, bool windowed); ~TransferFunctionEditor() = default; diff --git a/frontend/services/gui/src/windows/WindowCollection.cpp b/frontend/services/gui/src/windows/WindowCollection.cpp index 81cf99055c..05a9cde82e 100644 --- a/frontend/services/gui/src/windows/WindowCollection.cpp +++ b/frontend/services/gui/src/windows/WindowCollection.cpp @@ -12,6 +12,7 @@ #include "LogConsole.h" #include "ParameterList.h" #include "PerformanceMonitor.h" +#include "RenderingEndPoint.h" #include "TransferFunctionEditor.h" @@ -19,25 +20,101 @@ using namespace megamol; using namespace megamol::gui; -WindowCollection::WindowCollection() : windows() { +AbstractWindow::WindowType get_window_type(AbstractWindow::WindowConfigID id) { + switch (id) { + case AbstractWindow::WINDOW_ID_MAIN_PARAMETERS: { + PerformanceMonitor win("Performance Metrics"); + AbstractWindow::WindowType wt; + wt.unique = true; + wt.hotkey = win.Config().hotkey; + wt.name = win.Name(); + wt.id = AbstractWindow::WINDOW_ID_PERFORMANCE; + return wt; + } break; + case AbstractWindow::WINDOW_ID_PARAMETERS: { + + } break; + case AbstractWindow::WINDOW_ID_PERFORMANCE: { + PerformanceMonitor win("Performance Metrics"); + AbstractWindow::WindowType wt; + wt.unique = true; + wt.hotkey = win.Config().hotkey; + wt.name = win.Name(); + wt.id = AbstractWindow::WINDOW_ID_PERFORMANCE; + return wt; + } break; + case AbstractWindow::WINDOW_ID_HOTKEYEDITOR: { + HotkeyEditor win("Hotkey Editor"); + AbstractWindow::WindowType wt; + wt.unique = true; + wt.hotkey = win.Config().hotkey; + wt.name = win.Name(); + wt.id = AbstractWindow::WINDOW_ID_HOTKEYEDITOR; + return wt; + } break; + case AbstractWindow::WINDOW_ID_TRANSFER_FUNCTION: { + TransferFunctionEditor win("Transfer Function Editor", true); + AbstractWindow::WindowType wt; + wt.unique = true; + wt.hotkey = win.Config().hotkey; + wt.name = win.Name(); + wt.id = AbstractWindow::WINDOW_ID_TRANSFER_FUNCTION; + return wt; + } break; + case AbstractWindow::WINDOW_ID_CONFIGURATOR: { + auto tf = std::make_shared("Transfer Function Editor", true); + Configurator win("Configurator", tf); + AbstractWindow::WindowType wt; + wt.unique = true; + wt.hotkey = win.Config().hotkey; + wt.name = win.Name(); + wt.id = AbstractWindow::WINDOW_ID_TRANSFER_FUNCTION; + return wt; + } break; + case AbstractWindow::WINDOW_ID_LOGCONSOLE: { + LogConsole win("Log Console"); + AbstractWindow::WindowType wt; + wt.unique = true; + wt.hotkey = win.Config().hotkey; + wt.name = win.Name(); + wt.id = AbstractWindow::WINDOW_ID_LOGCONSOLE; + return wt; + } break; + case AbstractWindow::WINDOW_ID_RENDERING_ENDPOINT: { + AbstractWindow::WindowType wt; + wt.unique = false; + } break; + default: + return AbstractWindow::WindowType{}; + } +} + + +WindowCollection::WindowCollection() { - this->windows.emplace_back(std::make_shared("Hotkey Editor")); - this->windows.emplace_back(std::make_shared("Log Console")); - this->windows.emplace_back(std::make_shared("Transfer Function Editor", true)); - this->windows.emplace_back(std::make_shared("Performance Metrics")); - this->windows.emplace_back( + this->avail_windows.push_back(std::make_shared("Hotkey Editor")); + AddWindow("Hotkey Editor"); + this->avail_windows.push_back(std::make_shared("Log Console")); + AddWindow("Log Console"); + this->avail_windows.push_back(std::make_shared("Transfer Function Editor", true)); + AddWindow("Transfer Function Editor", true); + this->avail_windows.push_back(std::make_shared("Performance Metrics")); + AddWindow("Performance Metrics"); + this->avail_windows.push_back( std::make_shared("Configurator", this->GetWindow())); + AddWindow("Configurator", this->GetWindow()); + this->avail_windows.push_back(std::make_shared("Rendering Endpoint")); // Requires Configurator and TFEditor to be added before this->add_parameter_window("Parameters", AbstractWindow::WINDOW_ID_MAIN_PARAMETERS); // Windows are sorted depending on hotkey - std::sort(this->windows.begin(), this->windows.end(), + std::sort(this->avail_windows.begin(), this->avail_windows.end(), [&](std::shared_ptr const& a, std::shared_ptr const& b) { return (a->Config().hotkey.key > b->Config().hotkey.key); }); // retrieve resource requests of each window class - for (auto const& win : windows) { + for (auto const& win : avail_windows) { auto res = win->requested_lifetime_resources(); requested_resources.insert(requested_resources.end(), res.begin(), res.end()); } @@ -57,14 +134,14 @@ bool WindowCollection::AddWindow( auto win_hash = std::hash()(window_name); if (this->WindowExists(win_hash)) { // Overwrite volatile callback for existing window - for (auto& win : this->windows) { + for (auto& [key, win] : this->created_windows) { if (win->Hash() == win_hash) { win->SetVolatileCallback(callback); continue; } } } else { - this->windows.push_back(std::make_shared( + this->created_windows[window_name] = (std::make_shared( window_name, const_cast&>(callback))); } return true; @@ -74,7 +151,7 @@ bool WindowCollection::AddWindow( void WindowCollection::Update() { // Call window update functions - for (auto& win : this->windows) { + for (auto& [key, win] : this->created_windows) { win->Update(); } } @@ -123,7 +200,7 @@ void WindowCollection::Draw(bool menu_visible) { } }; - this->EnumWindows(func); + this->EnumCreatedWindows(func); } @@ -166,7 +243,7 @@ bool WindowCollection::StateFromJSON(const nlohmann::json& in_json) { } // Then read configuration for all existing windows - for (auto& window : this->windows) { + for (auto& [key, window] : this->created_windows) { window->StateFromJSON(in_json); window->SpecificStateFromJSON(in_json); } @@ -188,7 +265,7 @@ bool WindowCollection::StateToJSON(nlohmann::json& inout_json) { try { // Append to given json - for (auto& window : this->windows) { + for (auto& [key, window] : this->created_windows) { inout_json[GUI_JSON_TAG_WINDOW_CONFIGS][window->Name()]["win_callback"] = static_cast(window->WindowID()); /// XXX rename to "win_config_id" @@ -210,12 +287,20 @@ bool WindowCollection::StateToJSON(nlohmann::json& inout_json) { bool WindowCollection::DeleteWindow(size_t win_hash_id) { - - for (auto iter = this->windows.begin(); iter != this->windows.end(); iter++) { - if (((*iter)->Hash() == win_hash_id)) { - if (((*iter)->WindowID() == AbstractWindow::WINDOW_ID_VOLATILE) || - ((*iter)->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS)) { - this->windows.erase(iter); + /*windows.erase(windows.begin(), std::remove_if(windows.begin(), windows.end(), [&win_hash_id](auto& entry) { + if (((entry.second)->Hash() == win_hash_id)) { + if (((entry.second)->WindowID() == AbstractWindow::WINDOW_ID_VOLATILE) || + ((entry.second)->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS)) { + return true; + } + } + return false; + }));*/ + for (auto iter = this->created_windows.begin(); iter != this->created_windows.end(); iter++) { + if (((iter->second)->Hash() == win_hash_id)) { + if (((iter->second)->WindowID() == AbstractWindow::WINDOW_ID_VOLATILE) || + ((iter->second)->WindowID() == AbstractWindow::WINDOW_ID_PARAMETERS)) { + this->created_windows.erase(iter->second->Name()); return true; } else { megamol::core::utility::log::Log::DefaultLog.WriteError( @@ -231,12 +316,19 @@ bool WindowCollection::DeleteWindow(size_t win_hash_id) { void megamol::gui::WindowCollection::setRequestedResources( std::shared_ptr const& resources) { - for (auto& win : windows) { + for (auto& [key, win] : created_windows) { win->setRequestedResources(resources); } } +void megamol::gui::WindowCollection::digestChangedRequestedResources() { + for (auto& [key, win] : created_windows) { + win->digestChangedRequestedResources(); + } +} + + void WindowCollection::add_parameter_window( const std::string& window_name, AbstractWindow::WindowConfigID win_id, ImGuiID initial_module_uid) { @@ -246,6 +338,6 @@ void WindowCollection::add_parameter_window( [&](const std::string& windowname, AbstractWindow::WindowConfigID winid, ImGuiID initialmoduleuid) { this->add_parameter_window(windowname, winid, initialmoduleuid); }); - this->windows.emplace_back(win_paramlist); + this->created_windows[window_name] = win_paramlist; } } diff --git a/frontend/services/gui/src/windows/WindowCollection.h b/frontend/services/gui/src/windows/WindowCollection.h index 1aa86b8e3c..7e0166b084 100644 --- a/frontend/services/gui/src/windows/WindowCollection.h +++ b/frontend/services/gui/src/windows/WindowCollection.h @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include namespace megamol { @@ -40,17 +42,39 @@ class WindowCollection { bool AddWindow(const std::string& window_name, const std::function& callback); - inline void EnumWindows(const std::function& cb) { - // Needs fixed size if window is added while looping - auto window_count = this->windows.size(); - for (size_t i = 0; i < window_count; i++) { - cb((*this->windows[i])); + template + bool AddWindow(const std::string& window_name, Args... args) { + if (window_name.empty()) { + megamol::core::utility::log::Log::DefaultLog.WriteWarn( + "[GUI] Invalid window name. [%s, %s, line %d]\n", __FILE__, __FUNCTION__, __LINE__); + return false; + } + this->created_windows[window_name] = std::make_shared(window_name, std::forward(args)...); + return true; + } + + //inline void EnumWindows(const std::function& cb) { + // // Needs fixed size if window is added while looping + // for (auto& [key, val] : this->windows) { + // cb((*val)); + // } + //} + + void EnumAvailWindows(const std::function& cb) { + for (auto& win : avail_windows) { + cb(*win); + } + } + + void EnumCreatedWindows(const std::function& cb) { + for (auto& [key, val] : this->created_windows) { + cb((*val)); } } inline bool WindowExists(size_t hash_id) const { - for (auto& wc : this->windows) { - if (wc->Hash() == hash_id) + for (auto& [key, val] : this->created_windows) { + if (val->Hash() == hash_id) return true; } return false; @@ -58,7 +82,7 @@ class WindowCollection { template std::shared_ptr GetWindow() const { - for (auto& win_ptr : this->windows) { + for (auto& [key, win_ptr] : this->created_windows) { if (auto ret_win_ptr = std::dynamic_pointer_cast(win_ptr)) return ret_win_ptr; } @@ -73,10 +97,16 @@ class WindowCollection { void setRequestedResources(std::shared_ptr const& resources); + void digestChangedRequestedResources(); + private: // VARIABLES ------------------------------------------------------ - std::vector> windows; + std::set registered_windows; + + std::vector> avail_windows; + + std::unordered_map> created_windows; std::vector requested_resources; diff --git a/frontend/services/gui/src/windows/WindowCollection2.cpp b/frontend/services/gui/src/windows/WindowCollection2.cpp new file mode 100644 index 0000000000..562b3342a1 --- /dev/null +++ b/frontend/services/gui/src/windows/WindowCollection2.cpp @@ -0,0 +1,8 @@ +#include "WindowCollection2.h" + + +void megamol::gui::WindowCollection2::Update() { + for (auto& [key, val] : windows_) { + val->Update(); + } +} diff --git a/frontend/services/gui/src/windows/WindowCollection2.h b/frontend/services/gui/src/windows/WindowCollection2.h new file mode 100644 index 0000000000..f68e5e094d --- /dev/null +++ b/frontend/services/gui/src/windows/WindowCollection2.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "AbstractWindow2.h" +#include "KeyboardMouseInput.h" + +namespace megamol::gui { +class WindowCollection2 { +public: + template + void RegisterWindowType() { + registered_types_[std::type_index(typeid(T))] = T::GetTypeInfo(); + } + + bool EnumRegisteredWindows(std::function const& func) const { + std::for_each( + registered_types_.begin(), registered_types_.end(), [&func](auto const& entry) { func(entry.second); }); + } + + bool EnumWindows(std::function const& func) const { + std::for_each(windows_.begin(), windows_.end(), [&func](auto const& entry) { func(*entry.second); }); + } + + bool EnumWindows(std::function const& func) { + std::for_each(windows_.begin(), windows_.end(), [&func](auto& entry) { func(*entry.second); }); + } + + template + void AddWindow(std::string const& name, Args... args) { + windows_[name] = std::make_shared(name, std::forward(args)...); + } + + void RemoveWindow(std::string const& name) { + windows_.erase(name); + } + + template + std::shared_ptr GetWindow(std::string const& name) { + return std::dynamic_pointer_cast(windows_[name]); + } + + void Update(); + +private: + std::unordered_map registered_types_; + + std::unordered_map> windows_; +}; +} // namespace megamol::gui diff --git a/frontend/services/gui/src/windows/WindowConfig.h b/frontend/services/gui/src/windows/WindowConfig.h new file mode 100644 index 0000000000..cd78988752 --- /dev/null +++ b/frontend/services/gui/src/windows/WindowConfig.h @@ -0,0 +1,19 @@ +#pragma once + +#include "mmcore/view/Input.h" + +#include + +namespace megamol::gui { +struct BasicConfig { + bool show = false; // [SAVED] show/hide window + ImGuiWindowFlags flags = 0; // [SAVED] imgui window flags + megamol::core::view::KeyCode hotkey; // [SAVED] hotkey for opening/closing window + ImVec2 position = ImVec2(0.0f, 0.0f); // [SAVED] position for reset on state loading (current position) + ImVec2 size = ImVec2(0.0f, 0.0f); // [SAVED] size for reset on state loading (current size) + ImVec2 reset_size = ImVec2(0.0f, 0.0f); // [SAVED] minimum window size for soft reset + ImVec2 reset_position = ImVec2(0.0f, 0.0f); // [SAVED] window position for minimize reset + bool collapsed = false; // [SAVED] flag indicating whether window is collapsed or not + bool reset_pos_size = true; // flag indicates whether to reset window position and size +}; +} // namespace megamol::gui diff --git a/frontend/services/image_presentation/ImagePresentation_Service.cpp b/frontend/services/image_presentation/ImagePresentation_Service.cpp index 3c3d1b2f88..8ebd624915 100644 --- a/frontend/services/image_presentation/ImagePresentation_Service.cpp +++ b/frontend/services/image_presentation/ImagePresentation_Service.cpp @@ -86,6 +86,23 @@ bool ImagePresentation_Service::init(const Config& config) { }; m_entry_points_registry_resource.get_entry_point = [&](auto const& name) { return get_entry_point(name); }; + m_entry_points_registry_resource.add_sink = [&](auto const& sink) { + return add_sink(sink) && tell_subscribers(ev::AddSink, {sink}); + }; + + m_entry_points_registry_resource.remove_sink = [&](auto const& name) { + return remove_sink(name) && tell_subscribers(ev::RemoveSink, {name}); + }; + + m_entry_points_registry_resource.bind_sink_entry_point = [&](std::string const& sink_name, + std::string const& ep_name) { + return bind_sink_to_ep(sink_name, ep_name) && tell_subscribers(ev::BindSink, {sink_name, ep_name}); + }; + m_entry_points_registry_resource.unbind_sink_entry_point = [&](std::string const& sink_name, + std::string const& ep_name) { + return unbind_sink_to_ep(sink_name, ep_name) && tell_subscribers(ev::UnbindSink, {sink_name, ep_name}); + }; + this->m_providedResourceReferences = { {"ImagePresentationEntryPoints", m_entry_points_registry_resource}, // used by MegaMolGraph to set entry points {"EntryPointToPNG_ScreenshotTrigger", m_entrypointToPNG_trigger}, @@ -203,18 +220,20 @@ void ImagePresentation_Service::PresentRenderedImages() { // this way sinks can access the current fbo size as previous_state m_global_framebuffer_events.clear(); - // pull result images into separate list - static std::vector wrapped_images; - wrapped_images.clear(); - wrapped_images.reserve(m_entry_points.size()); + // pull result images into separate lists + std::unordered_map> sink_img_map; + sink_img_map.reserve(m_presentation_sinks.size()); // rendering results are presented in order of execution of entry points for (auto& entry : m_entry_points) { - wrapped_images.push_back(entry.execution_result_image); + auto const& sink_list = ep_sink_map[entry.moduleName]; + for (auto const& sink : sink_list) { + sink_img_map[sink].push_back(entry.execution_result_image); + } } for (auto& sink : m_presentation_sinks) { - sink.present_images(wrapped_images); + sink.present_images(sink_img_map[sink.name]); } } @@ -331,6 +350,8 @@ bool ImagePresentation_Service::add_entry_point(std::string const& name, EntryPo // ensure sorting of entry points according to priorities set_entry_point_priority(name, 0); + bind_sink_to_ep(frontend_resources::ImagePresentationEntryPoints::GLFW_Sink_Name, name); + return true; } @@ -352,6 +373,8 @@ bool ImagePresentation_Service::set_entry_point_priority(std::string const& name bool ImagePresentation_Service::remove_entry_point(std::string const& name) { + ep_sink_map.erase(name); + m_entry_points.remove_if([&](auto& entry) { return entry.moduleName == name; }); return true; @@ -368,12 +391,53 @@ bool ImagePresentation_Service::rename_entry_point(std::string const& oldName, s entry_it->moduleName = newName; + auto l = ep_sink_map[oldName]; + ep_sink_map.erase(oldName); + ep_sink_map[newName] = l; + return true; } bool ImagePresentation_Service::clear_entry_points() { m_entry_points.clear(); + ep_sink_map.clear(); + + return true; +} + +bool ImagePresentation_Service::add_sink(ImagePresentationSink const& sink) { + m_presentation_sinks.push_back(sink); + return true; +} + +bool ImagePresentation_Service::remove_sink(std::string const& name) { + m_presentation_sinks.remove_if([&name](auto const& entry) { return entry.name == name; }); + for (auto& [key, list] : ep_sink_map) { + list.remove(name); + } + return true; +} + +bool ImagePresentation_Service::bind_sink_to_ep(std::string const& sink_name, std::string const& ep_name) { + auto& sink_list = ep_sink_map[ep_name]; + if (std::find(sink_list.begin(), sink_list.end(), sink_name) == sink_list.end()) { + sink_list.push_back(sink_name); + } + + return true; +} + +bool ImagePresentation_Service::unbind_sink_to_ep(std::string const& sink_name, std::string const& ep_name) { + if (ep_name.empty()) { + for (auto& [key, list] : ep_sink_map) { + list.remove(sink_name); + } + } else { + auto& sink_list = ep_sink_map[ep_name]; + sink_list.remove(sink_name); + } + return true; } @@ -386,8 +450,8 @@ void ImagePresentation_Service::add_glfw_sink() { return; } - m_presentation_sinks.push_back( - {"GLFW Window Presentation Sink", [&](auto const& images) { this->present_images_to_glfw_window(images); }}); + m_presentation_sinks.push_back({frontend_resources::ImagePresentationEntryPoints::GLFW_Sink_Name, + [&](auto const& images) { this->present_images_to_glfw_window(images); }}); } void ImagePresentation_Service::subscribe_to_entry_point_changes( diff --git a/frontend/services/image_presentation/ImagePresentation_Service.hpp b/frontend/services/image_presentation/ImagePresentation_Service.hpp index 7e77047cf8..e7bb4b8a82 100644 --- a/frontend/services/image_presentation/ImagePresentation_Service.hpp +++ b/frontend/services/image_presentation/ImagePresentation_Service.hpp @@ -106,6 +106,10 @@ class ImagePresentation_Service final : public AbstractFrontendService { bool remove_entry_point(std::string const& name); bool rename_entry_point(std::string const& oldName, std::string const& newName); bool clear_entry_points(); + bool add_sink(ImagePresentationSink const& sink); + bool remove_sink(std::string const& name); + bool bind_sink_to_ep(std::string const& sink_name, std::string const& ep_name); + bool unbind_sink_to_ep(std::string const& sink_name, std::string const& ep_name); void subscribe_to_entry_point_changes( frontend_resources::ImagePresentationEntryPoints::SubscriberFunction const& subscriber); @@ -135,6 +139,8 @@ class ImagePresentation_Service final : public AbstractFrontendService { void fill_lua_callbacks(); std::function m_entrypointToPNG_trigger; + + std::unordered_map> ep_sink_map; }; } // namespace frontend diff --git a/frontend/services/lua_service_wrapper/Lua_Service_Wrapper.cpp b/frontend/services/lua_service_wrapper/Lua_Service_Wrapper.cpp index 0fb8a38feb..febf5700b7 100644 --- a/frontend/services/lua_service_wrapper/Lua_Service_Wrapper.cpp +++ b/frontend/services/lua_service_wrapper/Lua_Service_Wrapper.cpp @@ -116,6 +116,7 @@ bool Lua_Service_Wrapper::init(const Config& config) { "RenderNextFrame", // LuaAPI can render one frame "GlobalValueStore", // LuaAPI can read and set global values frontend_resources::CommandRegistry_Req_Name, "optional", "RuntimeConfig", + "ImagePresentationEntryPoints" #ifdef MEGAMOL_USE_PROFILING frontend_resources::PerformanceManager_Req_Name #endif @@ -699,6 +700,37 @@ void Lua_Service_Wrapper::fill_graph_manipulation_callbacks(void* callbacks_coll } return VoidResult{}; }}); + callbacks.add("mmBindSink", + "(string sinkName, string moduleName)\n\tBind specified entry point to the sink.", + {[&](std::string sinkName, std::string moduleName) -> VoidResult { + auto& ep_ref = + m_requestedResourceReferences[11].getResource(); + auto res = ep_ref.bind_sink_entry_point(sinkName, moduleName); + if (!res) { + return Error{"Could not bind entry point " + moduleName + " to sink " + sinkName}; + } + return VoidResult{}; + }}); + callbacks.add("mmUnbindSink", + "(string sinkName, string moduleName)\n\tUnbind specified entry point from the sink.", + {[&](std::string sinkName, std::string moduleName) -> VoidResult { + auto& ep_ref = + m_requestedResourceReferences[11].getResource(); + auto res = ep_ref.unbind_sink_entry_point(sinkName, moduleName); + if (!res) { + return Error{"Could not unbind entry point " + moduleName + " from sink " + sinkName}; + } + return VoidResult{}; + }}); + callbacks.add("mmAddGraphEntryPoint", + "(string moduleName)\n\tAdd active graph entry point from one specific module.", + {[&](std::string moduleName) -> VoidResult { + auto res = graph.AddGraphEntryPoint(moduleName); + if (!res) { + return Error{"Could not add graph entry point " + moduleName}; + } + return VoidResult{}; + }}); callbacks.add("mmRemoveGraphEntryPoint", "(string moduleName)\n\tRemove active graph entry point from one specific module.", {[&](std::string moduleName) -> VoidResult { @@ -723,7 +755,7 @@ void Lua_Service_Wrapper::fill_graph_manipulation_callbacks(void* callbacks_coll callbacks.add("mmListModuleTimers", "(string name)\n\tList the registered timers of a module.", {[&](std::string name) -> StringResult { auto perf_manager = const_cast( - &this->m_requestedResourceReferences[11] + &this->m_requestedResourceReferences[12] .getResource()); std::stringstream output; auto m = graph.FindModule(name); @@ -742,7 +774,7 @@ void Lua_Service_Wrapper::fill_graph_manipulation_callbacks(void* callbacks_coll "(int handle, string comment)\n\tSet a transient comment for a timer; will show up in profiling log.", {[&](int handle, std::string comment) -> VoidResult { auto perf_manager = const_cast( - &this->m_requestedResourceReferences[11] + &this->m_requestedResourceReferences[12] .getResource()); perf_manager->set_transient_comment(handle, comment); return VoidResult{}; diff --git a/plugins/optix_hpg/src/optix/Context.cpp b/plugins/optix_hpg/src/optix/Context.cpp index 1d934dc8ee..46288033e4 100644 --- a/plugins/optix_hpg/src/optix/Context.cpp +++ b/plugins/optix_hpg/src/optix/Context.cpp @@ -35,7 +35,7 @@ megamol::optix_hpg::Context::Context(frontend_resources::CUDA_Context const& ctx _module_options.maxRegisterCount = OPTIX_COMPILE_DEFAULT_MAX_REGISTER_COUNT; #ifdef DEBUG _module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0; - _module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; + _module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_MODERATE; #else _module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_3; _module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_NONE;