Skip to content

Commit

Permalink
Merge pull request #121 from berdal84/feat/node-search
Browse files Browse the repository at this point in the history
feat(GraphView): advanced contextual menu, introducing Actions
  • Loading branch information
berdal84 committed Apr 26, 2024
2 parents bf19a7d + 5b48999 commit f1e57da
Show file tree
Hide file tree
Showing 44 changed files with 1,483 additions and 902 deletions.
10 changes: 6 additions & 4 deletions src/fw/core/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ namespace fw
{
namespace hash
{
inline static size_t hash(char* buffer, size_t buf_size, size_t seed = 0)
{ return XXHash32::hash(buffer, buf_size, seed); }
using hash_t = u32_t;

inline static size_t hash(const char* str, size_t seed = 0)
{ return XXHash32::hash(str, strlen(str), seed); }
inline static hash_t hash(char* buffer, size_t buf_size, uint32_t seed = 0)
{ return XXHash32::hash(buffer, (uint64_t)buf_size, seed); }

inline static hash_t hash(const char* str, uint32_t seed = 0)
{ return XXHash32::hash(str, (uint64_t)strlen(str), seed); }
};
}
14 changes: 9 additions & 5 deletions src/fw/gui-example/AppExampleView.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ namespace fw
dock_window( "top", fw::AppView::Dockspace_TOP );
}

void on_draw_splashscreen() override
void draw_splashscreen() override
{
ImGui::TextWrapped( "Welcome to the framework-gui-example app.\nThis demonstrates how to use the framework-gui library." );
ImGui::Separator();
ImGui::TextWrapped( "\nFor your information, this is the splashscreen window of the app.\n"
"You can inject your custom code by editing Example::on_draw_splashscreen()\n" );
if ( AppView::begin_splashscreen() )
{
ImGui::TextWrapped( "Welcome to the framework-gui-example app.\nThis demonstrates how to use the framework-gui library." );
ImGui::Separator();
ImGui::TextWrapped( "\nFor your information, this is the splashscreen window of the app.\n"
"You can inject your custom code by editing Example::draw_splashscreen()\n" );
AppView::end_splashscreen();
}
}
};
}
15 changes: 15 additions & 0 deletions src/fw/gui/Action.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "Action.h"
#include "EventManager.h"

using namespace fw;

void IAction::trigger() const
{
EventManager& event_manager = EventManager::get_instance();
event_manager.dispatch( make_event() );
}

IEvent* IAction::make_event() const
{
return new IEvent(event_id);
}
87 changes: 87 additions & 0 deletions src/fw/gui/Action.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#pragma once
#include "Event.h"
#include "core/types.h"
#include <SDL/include/SDL_keycode.h>
#include <string>

namespace fw
{
/** Data describing a shortcut (ex: "Reset": Ctrl + Alt + R) */
struct Shortcut
{
SDL_Keycode key = SDLK_UNKNOWN; // a key to be pressed
SDL_Keymod mod = KMOD_NONE; // modifiers (alt, ctrl, etc.)
std::string description;
std::string to_string() const;
};

/**
* The purpose of any IAction is to trigger a given basic event (identified by an EventID)
* when a key shortcut is pressed.
*/
class IAction
{
public:
explicit IAction(
EventID event_id,
const char* label = "action",
Shortcut&& shortcut = {},
u64_t userdata = {}
)
: label(label)
, event_id(event_id)
, shortcut(std::move(shortcut))
, userdata(userdata)
{}
std::string label;
EventID event_id;
Shortcut shortcut;
u64_t userdata;

void trigger() const; // Trigger action, will dispatch an event with default values
virtual IEvent* make_event() const; // Make a new event with default values
};

/**
* The purpose of an Action is similar to IAction for events requiring some data to be constructed
*/
template<typename EventT>
class Action : public IAction
{
public:
static_assert( !std::is_base_of_v<EventT, IEvent> ); // Ensure EventT implements IEvent

using event_t = EventT; // Type of the event triggered by this action
using event_data_t = typename EventT::data_t; // Type of the payload for the events triggered by this action

event_data_t event_data; // Initial data used when making a new event

Action(
const char* label,
Shortcut&& shortcut
)
: IAction(EventT::id, label, std::move(shortcut) )
{}

Action(
const char* label, // Text to display for this action
Shortcut&& shortcut, // Shortcut able to trigger this action
u64_t userdata // Custom user data (typically to store flags)
)
: IAction(EventT::id, label, std::move(shortcut), userdata)
{}

Action(
const char* label, // Text to display for this action
Shortcut&& shortcut, // Shortcut able to trigger this action
event_data_t event_data, // Initial data of a default event
u64_t userdata = {} // Custom user data (typically to store flags)
)
: IAction(EventT::id, label, std::move(shortcut), userdata)
, event_data( event_data )
{}

EventT* make_event() const override // Make a new event using default event_data
{ return new EventT( event_data ); }
};
}
73 changes: 73 additions & 0 deletions src/fw/gui/ActionManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "ActionManager.h"
#include "core/assertions.h"
#include "core/async.h"
#include "core/log.h"
#include "core/reflection/type.h"
#include <SDL/include/SDL_keyboard.h>
#include <future>
#include <thread>

using namespace fw;

ActionManager* ActionManager::s_instance = nullptr;

ActionManager::ActionManager()
{
LOG_VERBOSE("fw::ActionManager", "Constructor ...\n");
FW_EXPECT(!s_instance, "cannot have two instances at a time");
s_instance = this;
LOG_VERBOSE("fw::ActionManager", "Constructor " OK "\n");
}

ActionManager::~ActionManager()
{
LOG_VERBOSE("fw::ActionManager", "Destructor ...\n");
s_instance = nullptr;
for( auto action : m_actions )
{
delete action;
}
LOG_VERBOSE("fw::ActionManager", "Destructor " OK "\n");
}

ActionManager& ActionManager::get_instance()
{
FW_EXPECT(s_instance, "No instance found.");
return *s_instance;
}

const IAction* ActionManager::get_action_with_id(EventID id)
{
auto found = m_actions_by_id.find(id);
if ( found == m_actions_by_id.end() )
{
string128 str;
str.append_fmt("Unable to find an action bound to EventId %i\n", id);
FW_EXPECT(false, str.c_str() );
}
return found->second;
}

const std::vector<IAction*>& ActionManager::get_actions() const
{
return m_actions;
}

void ActionManager::add_action( IAction* _action )// Add a new action (can be triggered via shortcut)
{
m_actions.push_back( _action );
m_actions_by_id.insert(std::pair{_action->event_id, _action});
LOG_MESSAGE("ActionManager", "Action '%s' bound to the event_id %i\n", _action->label.c_str(), _action->event_id);
}

std::string Shortcut::to_string() const
{
std::string result;

if (mod & KMOD_CTRL) result += "Ctrl + ";
if (mod & KMOD_ALT) result += "Alt + ";
if (key) result += SDL_GetKeyName(key);
if (!description.empty()) result += description;

return result;
}
56 changes: 56 additions & 0 deletions src/fw/gui/ActionManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#pragma once

#include <queue>
#include <string>
#include <future>
#include <map>
#include <utility>

#include "Action.h"
#include "Event.h"
#include "core/reflection/func_type.h"
#include "core/types.h"

namespace fw
{
class ActionManager
{
public:
ActionManager();
ActionManager(const ActionManager&) = delete;
~ActionManager();

const std::vector<IAction*>& get_actions() const; // Get all the actions bound to any event

template<typename EventT, typename... Args>
Action<EventT>* new_action(Args&&... args)
{
static_assert(std::is_base_of_v<IEvent, EventT> );
auto* action = new Action<EventT>(std::forward<Args>(args)...);
add_action(action);
return action;
}

const IAction* get_action_with_id(EventID id); // Get the action bound to a given event type
static ActionManager& get_instance();

template<class ActionT>
static ActionT* get_action() // Helper to get a given action type from the ActionManager instance
{ return s_instance->get_action<ActionT>(); }

private:

template<class ActionT>
ActionT* _get_action() const
{
static_assert( std::is_base_of_v<IAction, ActionT> );
auto* action = get_action_with_id( ActionT::event_id );
return dynamic_cast<ActionT*>( action );
}

void add_action( IAction* _action);
static ActionManager* s_instance;
std::vector<IAction*> m_actions;
std::unordered_multimap<EventID, IAction*> m_actions_by_id;
};
}
33 changes: 33 additions & 0 deletions src/fw/gui/ActionManagerView.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "ActionManagerView.h"
#include "ActionManager.h"
#include "imgui.h"

using namespace fw;

void ActionManagerView::draw(ActionManager* manager)
{
if ( ImGui::BeginTable("Actions", 2) )
{
ImGui::TableSetupColumn("Action");
ImGui::TableSetupColumn("Shortcut");
ImGui::TableHeadersRow();

for( auto& action : manager->get_actions())
{
ImGui::PushID(action);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if( ImGui::SmallButton("trigger") )
{
action->trigger();
}
ImGui::SameLine();
ImGui::Text("%s", action->label.c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", action->shortcut.to_string().c_str()); // TODO: handle shortcut edition
ImGui::PopID();
}
ImGui::EndTable();
}
}

12 changes: 12 additions & 0 deletions src/fw/gui/ActionManagerView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

namespace fw
{
// Forward declaration
class ActionManager;

class ActionManagerView
{
public: static void draw(ActionManager* manager);
};
}
27 changes: 13 additions & 14 deletions src/fw/gui/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ App::App(Config& _config, AppView* _view)
, should_stop(false)
, font_manager(_config.font_manager)
, event_manager()
, action_manager()
, texture_manager()
, m_sdl_window(nullptr)
, m_sdl_gl_context()
Expand Down Expand Up @@ -240,31 +241,29 @@ void App::handle_events()
// With mode key only
if( event.key.keysym.mod & (KMOD_CTRL | KMOD_ALT) )
{
for(const auto& _binded_event: event_manager.get_binded_events() )
for(const IAction* _action: action_manager.get_actions() )
{
// first, priority to shortcuts with mod
if ( _binded_event.shortcut.mod != KMOD_NONE
&& _binded_event.event_t
&& (_binded_event.shortcut.mod & event.key.keysym.mod)
&& _binded_event.shortcut.key == event.key.keysym.sym
if ( _action->shortcut.mod != KMOD_NONE
&& _action->event_id && ( _action->shortcut.mod & event.key.keysym.mod)
&& _action->shortcut.key == event.key.keysym.sym
)
{
event_manager.push(_binded_event.event_t);
event_manager.dispatch( _action->event_id );
break;
}
}
}
else // without any mod key
{
for(const auto& _binded_event: event_manager.get_binded_events() )
for(const IAction* _action: action_manager.get_actions() )
{
// first, priority to shortcuts with mod
if ( _binded_event.shortcut.mod == KMOD_NONE
&& _binded_event.event_t
&& _binded_event.shortcut.key == event.key.keysym.sym
if ( _action->shortcut.mod == KMOD_NONE
&& _action->event_id && _action->shortcut.key == event.key.keysym.sym
)
{
event_manager.push(_binded_event.event_t);
event_manager.dispatch( _action->event_id );
break;
}
}
Expand Down Expand Up @@ -340,9 +339,9 @@ int App::main(int argc, char *argv[])
if( all_time <= 0 ) all_time = 1;
dt = u32_t(0.9f*float(dt) + 0.1f*float(all_time)); // Smooth value
u32_t fps = 1000 / dt;
char title[255];
snprintf( title, 255, "%s | %i fps (dt %d ms, frame %d ms)", config.app_window_label.c_str(), fps, dt, frame_time );
title[254] = '\0';
char title[256];
snprintf( title, 256, "%s | %i fps (dt %d ms, frame %d ms)", config.app_window_label.c_str(), fps, dt, frame_time );
title[255] = '\0';
SDL_SetWindowTitle( m_sdl_window, title );
}
}
Expand Down
Loading

0 comments on commit f1e57da

Please sign in to comment.