Node interactions working again, graph editing functions not complete yet
This commit is contained in:
+8
-115
@@ -2,17 +2,15 @@
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include "interaction-manager.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "clipboard.h"
|
||||
#include "datatypes.h"
|
||||
#include "canvas.h"
|
||||
#include "graph-manager.h"
|
||||
#include "interaction-event.h"
|
||||
#include "midi-node.h"
|
||||
#include "mouse.h"
|
||||
#include "node.h"
|
||||
#include "raylib.h"
|
||||
#include "selectable.h"
|
||||
#include "signal-manager.h"
|
||||
#include "state.h"
|
||||
#include "ui.h"
|
||||
@@ -30,18 +28,18 @@ namespace VISEQ::APP {
|
||||
class AppManager{
|
||||
public:
|
||||
|
||||
AppManager() : sm(&gm, &mutex), mouse(&gm), ui(&sm, &gm) {}
|
||||
AppManager() : sm(&gm, &mutex), im(&gm, &sm, canvas), mouse(&gm), ui(&sm, &gm) {}
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
std::shared_ptr<APP::State> state = APP::State::get();
|
||||
|
||||
std::shared_ptr<CANVAS::Canvas> canvas = std::make_shared<CANVAS::Canvas>();
|
||||
VISEQ::GRAPH::GraphManager gm;
|
||||
VISEQ::SIGNAL::SignalManager sm;
|
||||
VISEQ::APP::Clipboard cb;
|
||||
VISEQ::INTERACTION::InteractionManager im;
|
||||
VISEQ::APP::Mouse mouse;
|
||||
|
||||
std::shared_ptr<APP::State> state = APP::State::get();
|
||||
|
||||
UI::AppUI ui;
|
||||
|
||||
void init(){
|
||||
@@ -152,104 +150,6 @@ namespace VISEQ::APP {
|
||||
}
|
||||
}
|
||||
|
||||
void removeNodes(VEC_SPTR_Node nodes){
|
||||
|
||||
VEC_SPTR_Node adjacent_nodes = gm.getAdjacentNodes(nodes);
|
||||
|
||||
sm.rerouteSignal(nodes, adjacent_nodes);
|
||||
sm.rebaseSignal(nodes);
|
||||
|
||||
gm.removeNodesAndEdges(nodes);
|
||||
}
|
||||
|
||||
void doInteraction(){
|
||||
bool exit = false;
|
||||
bool once = false;
|
||||
state->mouse_z = canvas->z_index;
|
||||
|
||||
while(event_queue.size() > 0 && !exit){
|
||||
auto e = event_queue.front();
|
||||
std::cout << e.item->getType() << " " << e.action << std::endl;
|
||||
|
||||
if(e.action == Interaction::HOVER){
|
||||
state->mouse_z = e.item->z_index;
|
||||
std::cout << state->mouse_z << std::endl;
|
||||
}
|
||||
|
||||
if(e.item->getType() == GraphItemType::CANVAS) {
|
||||
if(e.action == Interaction::SELECT) {
|
||||
gm.select();
|
||||
std::cout << "app canvas select" << std::endl;
|
||||
canvas->clearSelectionRect();
|
||||
}
|
||||
if(e.action == Interaction::DESELECT) {
|
||||
std::cout << "app canvas deselect" << std::endl;
|
||||
gm.deselect();
|
||||
}
|
||||
}
|
||||
|
||||
if(!once && e.item->getType() == GraphItemType::NODE){
|
||||
auto node = std::dynamic_pointer_cast<Node>(e.item);
|
||||
if(e.action == Interaction::PICKUP) {
|
||||
node->pickUp();
|
||||
if(node->selected){
|
||||
for(auto s : gm.selected_nodes){
|
||||
s->pickUp(node->position);
|
||||
}
|
||||
}
|
||||
once = true;
|
||||
}
|
||||
if(e.action == Interaction::DRAG) {
|
||||
if(KEYS_DOWN.ctrl) node->drag(MOD_MOUSE);
|
||||
else node->drag(MOD_MOUSE + node->drag_offset);
|
||||
|
||||
if(node->selected){
|
||||
for(auto s : gm.selected_nodes){
|
||||
s->drag(MOD_MOUSE + s->drag_offset);
|
||||
}
|
||||
}
|
||||
once = true;
|
||||
}
|
||||
if(e.action == Interaction::PUTDOWN) {
|
||||
node->putDown();
|
||||
if(node->selected){
|
||||
for(auto s : gm.selected_nodes){
|
||||
s->putDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(exit) break;
|
||||
|
||||
if(!once && (e.item->getType() == GraphItemType::CONNECTOR || e.item->getType() == GraphItemType::CANVAS)) {
|
||||
switch(e.action){
|
||||
case VISEQ::APP::Interaction::PICKUP:
|
||||
{
|
||||
e.item->pickUp();
|
||||
once = true;
|
||||
break;
|
||||
}
|
||||
case VISEQ::APP::Interaction::DRAG:
|
||||
{
|
||||
e.item->drag(MOD_MOUSE);
|
||||
once = true;
|
||||
break;
|
||||
}
|
||||
case VISEQ::APP::Interaction::PUTDOWN:
|
||||
{
|
||||
e.item->putDown();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
event_queue.pop_front();
|
||||
}
|
||||
event_queue.clear();
|
||||
}
|
||||
|
||||
void update(){
|
||||
state->update(canvas->camera);
|
||||
canvas->update();
|
||||
@@ -258,18 +158,9 @@ namespace VISEQ::APP {
|
||||
updateMidiPorts();
|
||||
mutex.unlock();
|
||||
|
||||
doInteraction();
|
||||
mouse.update();
|
||||
im.update();
|
||||
|
||||
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_C)){
|
||||
VEC_SPTR_Node selected_nodes = gm.getSelection();
|
||||
if(!selected_nodes.empty()) cb.pushNodes(selected_nodes);
|
||||
}
|
||||
|
||||
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_V)){
|
||||
VEC_SPTR_Node paste_nodes = cb.pullNodes();
|
||||
if(!paste_nodes.empty()) gm.pasteNodesAndEdges(paste_nodes, MOD_MOUSE);
|
||||
}
|
||||
}
|
||||
|
||||
void draw(){
|
||||
@@ -282,6 +173,8 @@ namespace VISEQ::APP {
|
||||
gm.drawEdges(SCREEN_MOUSE, canvas->camera);
|
||||
sm.drawSignals(SCREEN_MOUSE, canvas->camera);
|
||||
|
||||
im.draw();
|
||||
|
||||
canvas->drawEnd();
|
||||
drawUI();
|
||||
EndDrawing();
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace VISEQ::GRAPH {
|
||||
if(new_partner) {
|
||||
//TODO what happens if a new partner is connected?
|
||||
new_partner = false;
|
||||
state->pushEvent({getPtr(), APP::Interaction::CONNECT});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ namespace VISEQ::GRAPH::NODES {
|
||||
|
||||
|
||||
namespace VISEQ::GRAPH{
|
||||
enum GraphItemType {NODE, EDGE, CONNECTOR, CANVAS};
|
||||
enum GraphItemType {NODE, EDGE, CONNECTOR, CANVAS, HANDLE};
|
||||
enum NodeType {OSCNODE, MIDINODE, CONFIGNODE};
|
||||
enum EdgeType {MIDIEDGE, CONFIGEDGE};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "connector.h"
|
||||
#include "graph-item.h"
|
||||
#include <memory>
|
||||
#include "node.h"
|
||||
|
||||
+43
-39
@@ -2,7 +2,6 @@
|
||||
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "draggable.h"
|
||||
#include "state.h"
|
||||
#include "vmath.h"
|
||||
#include "raylib.h"
|
||||
@@ -34,14 +33,19 @@ using namespace VISEQ::GRAPH::EDGES;
|
||||
|
||||
namespace VISEQ::GRAPH {
|
||||
|
||||
class GraphManager{
|
||||
class GraphManager : public std::enable_shared_from_this<GraphManager>{
|
||||
|
||||
public:
|
||||
|
||||
GraphManager() {}
|
||||
|
||||
std::shared_ptr<GraphManager> getPtr(){
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
NODES::VEC_SPTR_Node nodes;
|
||||
EDGES::VEC_SPTR_Edge edges;
|
||||
std::map<SPTR_Node, VEC_SPTR_Edge> connections;
|
||||
|
||||
EDGES::VEC_SPTR_Edge remove_edges;
|
||||
EDGES::VEC_SPTR_Edge split_edges;
|
||||
@@ -52,16 +56,24 @@ namespace VISEQ::GRAPH {
|
||||
|
||||
std::shared_ptr<APP::State> state = APP::State::get();
|
||||
|
||||
NODES::SPTR_Node getRandomNode(){
|
||||
VEC_SPTR_GraphItem o;
|
||||
std::ranges::sample(nodes, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
|
||||
return std::dynamic_pointer_cast<VISEQ::GRAPH::NODES::Node>(o.front());
|
||||
SPTR_Node addMidiNode(Vec2Df position){
|
||||
nodes.emplace_back(std::make_shared<MidiNode>(position));
|
||||
return nodes.back();
|
||||
}
|
||||
|
||||
EDGES::SPTR_Edge getRandomEdge(){
|
||||
VEC_SPTR_GraphItem o;
|
||||
std::ranges::sample(edges, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
|
||||
return std::dynamic_pointer_cast<VISEQ::GRAPH::EDGES::Edge>(o.front());
|
||||
void addNode(SPTR_Node node){
|
||||
nodes.insert(nodes.end(), node);
|
||||
nodes.back()->init();
|
||||
}
|
||||
|
||||
void addConnection(SPTR_Node from, SPTR_Node to){
|
||||
if(from != to) {
|
||||
edges.emplace_back(std::make_shared<Edge>());
|
||||
edges.back()->start = from;
|
||||
edges.back()->end = to;
|
||||
from->connections.push_back(edges.back());
|
||||
to->connections.push_back(edges.back());
|
||||
}
|
||||
}
|
||||
|
||||
void select(){
|
||||
@@ -85,11 +97,6 @@ namespace VISEQ::GRAPH {
|
||||
//return nodes | RANGE_FILTER([](SPTR_Node n){return n->selected;}) | std::ranges::to<VEC_SPTR_Node>();
|
||||
}
|
||||
|
||||
void addNode(SPTR_Node node){
|
||||
nodes.insert(nodes.end(), node);
|
||||
nodes.back()->init();
|
||||
}
|
||||
|
||||
void copyNodes(VEC_SPTR_Node _nodes, Vec2Df position){
|
||||
for(auto n: _nodes){
|
||||
nodes.insert(nodes.end(), std::make_shared<Node>(*n));
|
||||
@@ -120,11 +127,6 @@ namespace VISEQ::GRAPH {
|
||||
return nodes.back();
|
||||
}
|
||||
|
||||
SPTR_Node addMidiNode(Vec2Df position){
|
||||
nodes.emplace_back(std::make_shared<MidiNode>(position));
|
||||
return nodes.back();
|
||||
}
|
||||
|
||||
void setNodeMode(int mode){
|
||||
for(auto n: nodes){
|
||||
n->SMode(mode);
|
||||
@@ -135,11 +137,29 @@ namespace VISEQ::GRAPH {
|
||||
counter.clear();
|
||||
}
|
||||
|
||||
SPTR_Node next_node(SPTR_Node current, SPTR_Node last){
|
||||
std::vector<std::shared_ptr<Connector>> active_connectors;
|
||||
|
||||
NODES::SPTR_Node getRandomNode(){
|
||||
VEC_SPTR_GraphItem o;
|
||||
std::ranges::sample(nodes, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
|
||||
return std::dynamic_pointer_cast<VISEQ::GRAPH::NODES::Node>(o.front());
|
||||
}
|
||||
|
||||
EDGES::SPTR_Edge getRandomEdge(){
|
||||
VEC_SPTR_GraphItem o;
|
||||
std::ranges::sample(edges, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
|
||||
return std::dynamic_pointer_cast<VISEQ::GRAPH::EDGES::Edge>(o.front());
|
||||
}
|
||||
|
||||
SPTR_Node getClosestNode(Vec2Df position){
|
||||
auto neighbors = nodes | RANGE_FILTER([](SPTR_Node n){ return !n->picked_up;}) | std::ranges::to<VEC_SPTR_Node>();
|
||||
std::sort(neighbors.begin(), neighbors.end(), [position](SPTR_Node na, SPTR_Node nb){ return na->position.dist(position) < nb->position.dist(position);});
|
||||
return neighbors.front();
|
||||
}
|
||||
|
||||
/*SPTR_Node next_node(SPTR_Node current, SPTR_Node last){*/
|
||||
/* std::vector<std::shared_ptr<Connector>> active_connectors;*/
|
||||
/**/
|
||||
/*}*/
|
||||
|
||||
SPTR_Node next(SPTR_Node current, SPTR_Node last){
|
||||
VEC_SPTR_Edge possible_connections = current->connections | RANGE_FILTER([current](SPTR_Edge c){
|
||||
return (c->start == current && c->pass_forward) || (c->end == current && c->pass_backward);
|
||||
@@ -180,22 +200,6 @@ namespace VISEQ::GRAPH {
|
||||
return possible_connections.at(return_path)->getNextNode(current);
|
||||
}
|
||||
|
||||
SPTR_Node getClosestNode(Vec2Df position){
|
||||
auto neighbors = nodes | RANGE_FILTER([](SPTR_Node n){ return !n->picked_up;}) | std::ranges::to<VEC_SPTR_Node>();
|
||||
std::sort(neighbors.begin(), neighbors.end(), [position](SPTR_Node na, SPTR_Node nb){ return na->position.dist(position) < nb->position.dist(position);});
|
||||
return neighbors.front();
|
||||
}
|
||||
|
||||
void addConnection(SPTR_Node from, SPTR_Node to){
|
||||
if(from != to) {
|
||||
edges.emplace_back(std::make_shared<Edge>());
|
||||
edges.back()->start = from;
|
||||
edges.back()->end = to;
|
||||
from->connections.push_back(edges.back());
|
||||
to->connections.push_back(edges.back());
|
||||
}
|
||||
}
|
||||
|
||||
void splitEdge(SPTR_Edge edge){
|
||||
split_edges.push_back(edge);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace VISEQ::APP{
|
||||
|
||||
enum Interaction {NONE, HOVER, CLICK, HOLD, RELEASE, PICKUP, DRAG, PUTDOWN, SELECT, DESELECT};
|
||||
enum Interaction {NONE, HOVER, CLICK, HOLD, RELEASE, PICKUP, DRAG, PUTDOWN, SELECT, DESELECT, CONNECT};
|
||||
|
||||
struct InteractionEvent{
|
||||
std::shared_ptr<BASE::Interactable> item;
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
#pragma once
|
||||
|
||||
#include "clipboard.h"
|
||||
#include "connector.h"
|
||||
#include "datatypes.h"
|
||||
#include "draggable.h"
|
||||
#include "graph-item.h"
|
||||
#include "graph-manager.h"
|
||||
#include "interaction-event.h"
|
||||
#include "raylib.h"
|
||||
#include "signal-manager.h"
|
||||
#include "state.h"
|
||||
#include "canvas.h"
|
||||
#include <memory>
|
||||
|
||||
namespace VISEQ::INTERACTION{
|
||||
class ScaleHandle : public BASE::Draggable, public GraphItem{
|
||||
public:
|
||||
ScaleHandle(){}
|
||||
|
||||
float radius = 6;
|
||||
|
||||
std::shared_ptr<ScaleHandle> getPtr(){
|
||||
return std::static_pointer_cast<ScaleHandle>(shared_from_this());
|
||||
}
|
||||
|
||||
virtual GRAPH::GraphItemType getType() override {
|
||||
return GraphItemType::HANDLE;
|
||||
}
|
||||
|
||||
virtual bool hovering() override {
|
||||
return CheckCollisionPointCircle(SCREEN_MOUSE, position, radius);
|
||||
}
|
||||
|
||||
virtual void update() override {
|
||||
if(hovering()) state->pushEvent({getPtr(), APP::Interaction::HOVER});
|
||||
if(hovering() && clicked()) state->pushEvent({getPtr(), APP::Interaction::PICKUP});
|
||||
if(pickedUp()) state->pushEvent({getPtr(), APP::Interaction::DRAG});
|
||||
if(picked_up && released()) state->pushEvent({getPtr(), APP::Interaction::PUTDOWN});
|
||||
}
|
||||
|
||||
virtual void draw() override {
|
||||
DrawCircleV(position, radius, VS_COLOR_SUN);
|
||||
}
|
||||
|
||||
virtual bool select() override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class InteractionManager{
|
||||
public:
|
||||
InteractionManager(GRAPH::GraphManager *gm, SIGNAL::SignalManager *sm, std::shared_ptr<APP::CANVAS::Canvas> canvas) : canvas(canvas), gm(gm), sm(sm) {}
|
||||
|
||||
std::shared_ptr<APP::CANVAS::Canvas> canvas;
|
||||
GraphManager *gm;
|
||||
SIGNAL::SignalManager *sm;
|
||||
VISEQ::APP::Clipboard cb;
|
||||
|
||||
std::shared_ptr<APP::State> state = APP::State::get();
|
||||
|
||||
Vec2Df scale_start;
|
||||
std::shared_ptr<ScaleHandle> scale_handle = std::make_shared<ScaleHandle>();
|
||||
|
||||
void update(){
|
||||
doInteraction();
|
||||
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_C)){
|
||||
VEC_SPTR_Node selected_nodes = gm->getSelection();
|
||||
if(!selected_nodes.empty()) cb.pushNodes(selected_nodes);
|
||||
}
|
||||
|
||||
if(KEYS_DOWN.ctrl && IsKeyPressed(KEY_V)){
|
||||
VEC_SPTR_Node paste_nodes = cb.pullNodes();
|
||||
if(!paste_nodes.empty()) gm->pasteNodesAndEdges(paste_nodes, MOD_MOUSE);
|
||||
}
|
||||
|
||||
if(IsKeyPressed(KEY_DELETE)){
|
||||
removeNodes(gm->selected_nodes | std::ranges::to<VEC_SPTR_Node>());
|
||||
}
|
||||
|
||||
if(IsKeyPressed(KEY_TAB)) {
|
||||
state->node_mode = (state->node_mode + 1) % 3;
|
||||
}
|
||||
|
||||
scaleSelection();
|
||||
}
|
||||
|
||||
void removeNodes(VEC_SPTR_Node nodes){
|
||||
|
||||
VEC_SPTR_Node adjacent_nodes = gm->getAdjacentNodes(nodes);
|
||||
|
||||
sm->rerouteSignal(nodes, adjacent_nodes);
|
||||
sm->rebaseSignal(nodes);
|
||||
|
||||
gm->removeNodesAndEdges(nodes);
|
||||
}
|
||||
|
||||
void scaleSelection(){
|
||||
if(!gm->selected_nodes.empty()){
|
||||
float offset = 30;
|
||||
Vec2Df corner_tl = {50000, 50000};
|
||||
Vec2Df corner_br = {-50000, -50000};
|
||||
std::shared_ptr<Node> node_tl;
|
||||
std::shared_ptr<Node> node_br;
|
||||
|
||||
for(auto n : gm->selected_nodes){
|
||||
float x = n->position.x;
|
||||
float y = n->position.y;
|
||||
|
||||
if(x < corner_tl.x && y < corner_tl.y) node_tl = n;
|
||||
if(x > corner_br.x && y > corner_br.y) node_br = n;
|
||||
|
||||
if(x <= corner_tl.x) corner_tl.x = x;
|
||||
if(x >= corner_br.x) corner_br.x = x;
|
||||
if(y <= corner_tl.y) corner_tl.y = y;
|
||||
if(y >= corner_br.y) corner_br.y = y;
|
||||
}
|
||||
corner_tl -= Vec2Df(offset, offset);
|
||||
corner_br += Vec2Df(offset, offset);
|
||||
state->bounding_box = (Rectangle){corner_tl.x, corner_tl.y, corner_br.x - corner_tl.x, corner_br.y - corner_tl.y};
|
||||
|
||||
scale_handle->update();
|
||||
|
||||
if(scale_handle->clicked()){
|
||||
scale_start = (corner_br - corner_tl);
|
||||
for(auto n : gm->selected_nodes){
|
||||
n->drag_offset = n->position - node_tl->position;
|
||||
}
|
||||
}
|
||||
if(scale_handle->picked_up){
|
||||
Vec2Df scale = (scale_handle->position - corner_tl) / scale_start;
|
||||
if(IsKeyDown(KEY_LEFT_SHIFT)) scale.y = scale.x;
|
||||
DrawText(std::format("{:.2f} {:.2f}", scale.x, scale.y).c_str(), corner_br.x+5, corner_br.y+5, 10, RED);
|
||||
for(auto n : gm->selected_nodes){
|
||||
n->position = node_tl->position + (n->drag_offset * scale);
|
||||
}
|
||||
} else {
|
||||
scale_handle->position = corner_br;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void snapPolar(std::shared_ptr<Node> dragNode){
|
||||
float steps = round((dragNode->neighbors.front()->position.dist(SCREEN_MOUSE)) / state->settings.grid) * state->settings.grid;
|
||||
if(dragNode->neighbors.size() > 1){
|
||||
Vec2Df dn1 = dragNode->neighbors[0]->position;
|
||||
Vec2Df dn2 = dragNode->neighbors[1]->position;
|
||||
Vec2Df v1_orth = dn1.center(dn2);
|
||||
|
||||
if(dn1.x < dn2.x) std::swap(dn1,dn2);
|
||||
|
||||
float dist = dn1.dist(dn2);
|
||||
float dist_orth = std::sqrt(std::pow(steps,2) - std::pow(dist/2,2));
|
||||
|
||||
if(SCREEN_MOUSE.y < v1_orth.y) dist_orth *= -1;
|
||||
|
||||
Vec2Df v1_orth_t = v1_orth + (dn1-dn2).norm().orth() * dist_orth;
|
||||
DrawLineV(dn1, dn2, VS_COLOR_RED);
|
||||
DrawLineV(v1_orth, v1_orth_t, VS_COLOR_RED);
|
||||
DrawCircleV(v1_orth, 3.0f, VS_COLOR_RED);
|
||||
dragNode->position = v1_orth_t;
|
||||
} else if(!dragNode->neighbors.empty()){
|
||||
float pol_r = steps;
|
||||
float pol_theta = Vector2Angle(Vec2Df{1,0},(SCREEN_MOUSE - dragNode->neighbors.front()->position));
|
||||
Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta);
|
||||
dragNode->position = dragNode->neighbors.front()->position + polar;
|
||||
}
|
||||
}
|
||||
|
||||
void doInteraction(){
|
||||
bool exit = false;
|
||||
bool once = false;
|
||||
state->mouse_z = canvas->z_index;
|
||||
|
||||
while(APP::event_queue.size() > 0 && !exit){
|
||||
auto e = APP::event_queue.front();
|
||||
std::cout << e.item->getType() << " " << e.action << std::endl;
|
||||
|
||||
if(e.action == APP::Interaction::CONNECT){
|
||||
auto connector = std::dynamic_pointer_cast<Connector>(e.item);
|
||||
auto connector_to = std::dynamic_pointer_cast<Connector>(connector->partner);
|
||||
gm->addConnection(std::dynamic_pointer_cast<Node>(connector->parent), std::dynamic_pointer_cast<Node>(connector_to->parent));
|
||||
}
|
||||
|
||||
if(e.action == APP::Interaction::HOVER){
|
||||
state->mouse_z = e.item->z_index;
|
||||
std::cout << state->mouse_z << std::endl;
|
||||
}
|
||||
|
||||
if(e.item->getType() == GraphItemType::CANVAS) {
|
||||
if(e.action == APP::Interaction::SELECT) {
|
||||
gm->select();
|
||||
std::cout << "app canvas select" << std::endl;
|
||||
canvas->clearSelectionRect();
|
||||
}
|
||||
if(e.action == APP::Interaction::DESELECT) {
|
||||
std::cout << "app canvas deselect" << std::endl;
|
||||
gm->deselect();
|
||||
}
|
||||
}
|
||||
|
||||
if(!once && e.item->getType() == GraphItemType::NODE){
|
||||
auto node = std::dynamic_pointer_cast<Node>(e.item);
|
||||
if(e.action == APP::Interaction::PICKUP) {
|
||||
node->pickUp();
|
||||
if(node->selected){
|
||||
for(auto s : gm->selected_nodes){
|
||||
s->pickUp(node->position);
|
||||
}
|
||||
}
|
||||
once = true;
|
||||
}
|
||||
if(e.action == APP::Interaction::DRAG) {
|
||||
if(KEYS_DOWN.ctrl) node->drag(MOD_MOUSE);
|
||||
else if(KEYS_DOWN.shift) {
|
||||
snapPolar(node);
|
||||
}
|
||||
else node->drag(MOD_MOUSE + node->drag_offset);
|
||||
|
||||
if(node->selected){
|
||||
for(auto s : gm->selected_nodes){
|
||||
s->drag(MOD_MOUSE + s->drag_offset);
|
||||
}
|
||||
}
|
||||
once = true;
|
||||
}
|
||||
if(e.action == APP::Interaction::PUTDOWN) {
|
||||
node->putDown();
|
||||
if(node->selected){
|
||||
for(auto s : gm->selected_nodes){
|
||||
s->putDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(exit) break;
|
||||
|
||||
if(!once && (e.item->getType() == GraphItemType::CONNECTOR || e.item->getType() == GraphItemType::CANVAS || e.item->getType() == GraphItemType::HANDLE)) {
|
||||
switch(e.action){
|
||||
case VISEQ::APP::Interaction::PICKUP:
|
||||
{
|
||||
e.item->pickUp();
|
||||
once = true;
|
||||
break;
|
||||
}
|
||||
case VISEQ::APP::Interaction::DRAG:
|
||||
{
|
||||
e.item->drag(MOD_MOUSE);
|
||||
once = true;
|
||||
break;
|
||||
}
|
||||
case VISEQ::APP::Interaction::PUTDOWN:
|
||||
{
|
||||
e.item->putDown();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
APP::event_queue.pop_front();
|
||||
}
|
||||
APP::event_queue.clear();
|
||||
}
|
||||
|
||||
void draw(){
|
||||
if(!gm->selected_nodes.empty()){
|
||||
DrawRectangleRoundedLines(state->bounding_box, 0.1f, 3, VS_COLOR_LIGHTGREY);
|
||||
scale_handle->draw();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
-280
@@ -61,104 +61,7 @@ int main(){
|
||||
app.update();
|
||||
app.draw();
|
||||
|
||||
/*else if(IsKeyDown(KEY_LEFT_SHIFT)){*/
|
||||
/* float steps = round((dragNode->neighbors.front()->position.dist(mouse_pos)) / state->settings.grid) * state->settings.grid;*/
|
||||
/* if(dragNode->neighbors.size() > 1){*/
|
||||
/* Vec2Df dn1 = dragNode->neighbors[0]->position;*/
|
||||
/* Vec2Df dn2 = dragNode->neighbors[1]->position;*/
|
||||
/* Vec2Df v1_orth = dn1.center(dn2);*/
|
||||
/**/
|
||||
/* if(dn1.x < dn2.x) std::swap(dn1,dn2);*/
|
||||
/**/
|
||||
/* float dist = dn1.dist(dn2);*/
|
||||
/* float dist_orth = std::sqrt(std::pow(steps,2) - std::pow(dist/2,2));*/
|
||||
/**/
|
||||
/* if(mouse_pos.y < v1_orth.y) dist_orth *= -1;*/
|
||||
/**/
|
||||
/* Vec2Df v1_orth_t = v1_orth + (dn1-dn2).norm().orth() * dist_orth;*/
|
||||
/* DrawLineV(dn1, dn2, VS_COLOR_RED);*/
|
||||
/* DrawLineV(v1_orth, v1_orth_t, VS_COLOR_RED);*/
|
||||
/* DrawCircleV(v1_orth, 3.0f, VS_COLOR_RED);*/
|
||||
/* dragNode->position = v1_orth_t;*/
|
||||
/* } else if(!dragNode->neighbors.empty()){*/
|
||||
/* float pol_r = steps;*/
|
||||
/* float pol_theta = Vector2Angle(Vec2Df{1,0},(mouse_pos - dragNode->neighbors.front()->position));*/
|
||||
/* Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta); */
|
||||
/* dragNode->position = dragNode->neighbors.front()->position + polar;*/
|
||||
/* }*/
|
||||
// auto dragging_nodes = app.gm.nodes | RANGE_FILTER([](SPTR_Node n) { return n->dragging;});
|
||||
/*auto connecting_nodes = app.gm.nodes | RANGE_FILTER([](SPTR_Node n) { return n->connecting;});*/
|
||||
/*auto selected_nodes = app.gm.nodes | RANGE_FILTER([](SPTR_Node n) { return n->selected && !n->picked_up;}) | std::ranges::to<VEC_SPTR_Node>();*/
|
||||
/*auto hovering_nodes = app.gm.nodes | RANGE_FILTER([](SPTR_Node n) { return n->hovering();});*/
|
||||
/*auto hovering_connectors = app.gm.nodes | RANGE_FILTER([&mouse_pos](SPTR_Node n) { return CheckCollisionPointCircle(mouse_pos, (n->position - ((n->position - mouse_pos)).norm() * (n->radius + 14.0f)), 8.0f);});*/
|
||||
/**/
|
||||
/*for(auto &n : app.gm.nodes ){*/
|
||||
/* if(hovering_connectors.empty()) n->hover_connect = false;*/
|
||||
/*}*/
|
||||
/**/
|
||||
/*if(istate != InteractionState::ADD_NODES_MENU && !mouse_down && !mouse_right_down && dragging_nodes.empty() && connecting_nodes.empty() && hovering_nodes.empty() && hovering_connectors.empty()) istate = InteractionState::HOVER_CANVAS;*/
|
||||
/*if((mouse_clicked || mouse_down || mouse_released) && !mouse_right_down && dragging_nodes.empty() && connecting_nodes.empty() && hovering_nodes.empty() && hovering_connectors.empty() && istate != InteractionState::SCALE_SELECTION) istate = InteractionState::DRAW_SELECTION;*/
|
||||
/**/
|
||||
/*if(!hovering_nodes.empty() && !mouse_down && !mouse_right_down) istate = InteractionState::HOVER_NODE;*/
|
||||
/*if(!dragging_nodes.empty()) istate = InteractionState::DRAG_NODE; */
|
||||
/*if(!hovering_connectors.empty() && !mouse_right_down && istate == InteractionState::HOVER_CANVAS) istate = InteractionState::HOVER_CONNECTOR;*/
|
||||
/*if(!connecting_nodes.empty()) istate = InteractionState::CONNECT_NODE;*/
|
||||
/**/
|
||||
/*if(!selected_nodes.empty() && IsKeyPressed(KEY_DELETE)) istate = InteractionState::DELETE_SELECTED_NODES;*/
|
||||
/*if(!selected_nodes.empty() && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_C)) istate = InteractionState::COPY_SELECTED_NODES;*/
|
||||
/**/
|
||||
/*if(!copied_nodes.empty() && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_V)) istate = InteractionState::PASTE_SELECTED_NODES;*/
|
||||
/**/
|
||||
/*if(IsKeyPressed(KEY_TAB)) {*/
|
||||
/* state->settings.mode = (state->settings.mode + 1) % 3;*/
|
||||
/* app.gm.setNodeMode(state->settings.mode);*/
|
||||
/*}*/
|
||||
|
||||
/*if(!selected_nodes.empty()){*/
|
||||
/* float offset = 30;*/
|
||||
/* Vec2Df corner_tl = {50000, 50000};*/
|
||||
/* Vec2Df corner_br = {-50000, -50000};*/
|
||||
/* std::shared_ptr<Node> node_tl;*/
|
||||
/* std::shared_ptr<Node> node_br;*/
|
||||
/**/
|
||||
/* for(auto n : selected_nodes){*/
|
||||
/* float x = n->position.x;*/
|
||||
/* float y = n->position.y;*/
|
||||
/**/
|
||||
/* if(x < corner_tl.x && y < corner_tl.y) node_tl = n;*/
|
||||
/* if(x > corner_br.x && y > corner_br.y) node_br = n;*/
|
||||
/**/
|
||||
/* if(x <= corner_tl.x) corner_tl.x = x;*/
|
||||
/* if(x >= corner_br.x) corner_br.x = x;*/
|
||||
/* if(y <= corner_tl.y) corner_tl.y = y;*/
|
||||
/* if(y >= corner_br.y) corner_br.y = y;*/
|
||||
/* }*/
|
||||
/* corner_tl -= Vec2Df(offset, offset);*/
|
||||
/* corner_br += Vec2Df(offset, offset);*/
|
||||
/**/
|
||||
/* DrawRectangleLines(corner_tl.x, corner_tl.y, (corner_br.x - corner_tl.x), (corner_br.y - corner_tl.y), VS_COLOR_LIGHTGREY);*/
|
||||
/* DrawCircleV(corner_br, 5, YELLOW);*/
|
||||
/**/
|
||||
/* if(istate == InteractionState::SCALE_SELECTION && mouse_released){*/
|
||||
/* istate = InteractionState::HOVER_CANVAS;*/
|
||||
/* }*/
|
||||
/* if(mouse_clicked && corner_br.dist(mouse_pos) < 10){*/
|
||||
/* scale_start = (corner_br - corner_tl);*/
|
||||
/* for(auto n : selected_nodes){*/
|
||||
/* n->drag_offset = n->position - node_tl->position;*/
|
||||
/* }*/
|
||||
/* istate = InteractionState::SCALE_SELECTION;*/
|
||||
/* }*/
|
||||
/* if(istate == InteractionState::SCALE_SELECTION && mouse_down){*/
|
||||
/* Vec2Df scale = (mouse_pos - corner_tl) / scale_start;*/
|
||||
/* if(IsKeyDown(KEY_LEFT_SHIFT)) scale.y = scale.x;*/
|
||||
/* DrawText(std::format("{:.2f} {:.2f}", scale.x, scale.y).c_str(), corner_br.x+5, corner_br.y+5, 10, RED);*/
|
||||
/* for(auto n : selected_nodes){*/
|
||||
/* n->position = node_tl->position + (n->drag_offset * scale);*/
|
||||
/* }*/
|
||||
/* istate = InteractionState::SCALE_SELECTION;*/
|
||||
/* }*/
|
||||
/*}*/
|
||||
|
||||
/*switch(istate){*/
|
||||
/* case InteractionState::SCALE_SELECTION:*/
|
||||
@@ -186,189 +89,6 @@ int main(){
|
||||
/* DrawCircleLines(node_menu_position.x, node_menu_position.y, 32, VS_COLOR_DARKGREY);*/
|
||||
/**/
|
||||
/* }*/
|
||||
/* case InteractionState::DRAW_SELECTION:*/
|
||||
/* {*/
|
||||
/* if(mouse_clicked){*/
|
||||
/* select_start = mouse_pos;*/
|
||||
/* }*/
|
||||
/* if(mouse_down){*/
|
||||
/* Vec2Df select1 = {mouse_pos.x > select_start.x ? select_start.x : mouse_pos.x, mouse_pos.y > select_start.y ? select_start.y : mouse_pos.y};*/
|
||||
/* Vec2Df select2 = {mouse_pos.x > select_start.x ? mouse_pos.x - select_start.x : select_start.x - mouse_pos.x, mouse_pos.y > select_start.y ? mouse_pos.y - select_start.y : select_start.y - mouse_pos.y};*/
|
||||
/* DrawRectangleLines(select1.x, select1.y, select2.x, select2.y, (Color){150,150,150,150});*/
|
||||
/* selection = (Rectangle){select1.x, select1.y, select2.x, select2.y};*/
|
||||
/**/
|
||||
/* for(auto& n : app.gm.nodes){*/
|
||||
/* if(CheckCollisionPointRec(n->position, selection)) {*/
|
||||
/* n->in_rect = true;*/
|
||||
/* } else {*/
|
||||
/* n->in_rect = false;*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/**/
|
||||
/* if(mouse_released){ */
|
||||
/* int n_selected = 0;*/
|
||||
/* for(auto& n : app.gm.nodes){*/
|
||||
/* if(CheckCollisionPointRec(n->position, selection)) {*/
|
||||
/* n->selected = true;*/
|
||||
/* n->in_rect = false;*/
|
||||
/* n_selected++;*/
|
||||
/* } else {*/
|
||||
/* if(!IsKeyDown(KEY_LEFT_SHIFT)) n->selected = false;*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/* if(n_selected > 0) copied_nodes.clear();*/
|
||||
/* selection = (Rectangle){0,0,0,0};*/
|
||||
/* }*/
|
||||
/* break;*/
|
||||
/* }*/
|
||||
/* case InteractionState::HOVER_NODE:*/
|
||||
/* {*/
|
||||
/* if(mouse_clicked) {*/
|
||||
/* if(IsKeyDown(KEY_LEFT_SHIFT)) {*/
|
||||
/* hovering_nodes.front()->selected = true;*/
|
||||
/* } else {*/
|
||||
/* hovering_nodes.front()->dragging = true;*/
|
||||
/* hovering_nodes.front()->drag_offset = hovering_nodes.front()->position - mouse_pos;*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/* if(mouse_right_clicked) hovering_nodes.front()->config = true;*/
|
||||
/* break;*/
|
||||
/* }*/
|
||||
/* case InteractionState::DRAG_NODE:*/
|
||||
/* {*/
|
||||
/* if(!dragging_nodes.empty()){*/
|
||||
/* SPTR_Node dragNode = dragging_nodes.front();*/
|
||||
/**/
|
||||
/* for(auto &n : selected_nodes){*/
|
||||
/* if(!n->picked_up) n->drag_offset = n->position - dragNode->position;*/
|
||||
/* n->picked_up = true;*/
|
||||
/* }*/
|
||||
/**/
|
||||
/* if(IsKeyDown(KEY_LEFT_CONTROL)) {*/
|
||||
/* dragNode->position.x = round((mouse_pos.x + dragNode->drag_offset.x) / state->settings.grid) * state->settings.grid;*/
|
||||
/* dragNode->position.y = round((mouse_pos.y + dragNode->drag_offset.y) / state->settings.grid) * state->settings.grid;*/
|
||||
/* } else if(IsKeyDown(KEY_LEFT_SHIFT)){*/
|
||||
/* float steps = round((dragNode->neighbors.front()->position.dist(mouse_pos)) / state->settings.grid) * state->settings.grid;*/
|
||||
/* if(dragNode->neighbors.size() > 1){*/
|
||||
/* Vec2Df dn1 = dragNode->neighbors[0]->position;*/
|
||||
/* Vec2Df dn2 = dragNode->neighbors[1]->position;*/
|
||||
/* Vec2Df v1_orth = dn1.center(dn2);*/
|
||||
/**/
|
||||
/* if(dn1.x < dn2.x) std::swap(dn1,dn2);*/
|
||||
/**/
|
||||
/* float dist = dn1.dist(dn2);*/
|
||||
/* float dist_orth = std::sqrt(std::pow(steps,2) - std::pow(dist/2,2));*/
|
||||
/**/
|
||||
/* if(mouse_pos.y < v1_orth.y) dist_orth *= -1;*/
|
||||
/**/
|
||||
/* Vec2Df v1_orth_t = v1_orth + (dn1-dn2).norm().orth() * dist_orth;*/
|
||||
/* DrawLineV(dn1, dn2, VS_COLOR_RED);*/
|
||||
/* DrawLineV(v1_orth, v1_orth_t, VS_COLOR_RED);*/
|
||||
/* DrawCircleV(v1_orth, 3.0f, VS_COLOR_RED);*/
|
||||
/* dragNode->position = v1_orth_t;*/
|
||||
/* } else if(!dragNode->neighbors.empty()){*/
|
||||
/* float pol_r = steps;*/
|
||||
/* float pol_theta = Vector2Angle(Vec2Df{1,0},(mouse_pos - dragNode->neighbors.front()->position));*/
|
||||
/* Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta); */
|
||||
/* dragNode->position = dragNode->neighbors.front()->position + polar;*/
|
||||
/* }*/
|
||||
/* } else {*/
|
||||
/* dragNode->position = mouse_pos + dragNode->drag_offset;*/
|
||||
/* }*/
|
||||
/**/
|
||||
/* for(auto &n : selected_nodes){*/
|
||||
/* n->position = dragNode->position + n->drag_offset;*/
|
||||
/* }*/
|
||||
/**/
|
||||
/* if(mouse_released) {*/
|
||||
/* for(auto &n : dragging_nodes){*/
|
||||
/* n->dragging = false;*/
|
||||
/* n->picked_up = false;*/
|
||||
/* }*/
|
||||
/* for(auto &n : selected_nodes){*/
|
||||
/* n->picked_up = false;*/
|
||||
/* n->dragging = false;*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/* break;*/
|
||||
/* }*/
|
||||
/* case InteractionState::HOVER_CONNECTOR:*/
|
||||
/* {*/
|
||||
/* if(!hovering_connectors.empty()){*/
|
||||
/* hovering_connectors.front()->hover_connect = true;*/
|
||||
/* if(mouse_clicked) hovering_connectors.front()->connecting = true;*/
|
||||
/* }*/
|
||||
/* break;*/
|
||||
/* }*/
|
||||
/* case InteractionState::CONNECT_NODE:*/
|
||||
/* {*/
|
||||
/* Vec2Df connector_position = connecting_nodes.front()->position - ((connecting_nodes.front()->position - mouse_pos)).norm() * (connecting_nodes.front()->radius + 14.0f);*/
|
||||
/* Vec2Df target_position = mouse_pos;*/
|
||||
/**/
|
||||
/* if(IsKeyDown(KEY_LEFT_CONTROL)) {*/
|
||||
/* target_position.x = round((mouse_pos.x) / state->settings.grid) * state->settings.grid;*/
|
||||
/* target_position.y = round((mouse_pos.y) / state->settings.grid) * state->settings.grid;*/
|
||||
/* } else if(IsKeyDown(KEY_LEFT_SHIFT)){*/
|
||||
/* std::shared_ptr<Node> closest = app.gm.getClosestNode(mouse_pos);*/
|
||||
/* float steps = round((closest->position.dist(mouse_pos)) / state->settings.grid) * state->settings.grid;*/
|
||||
/* float pol_r = steps;*/
|
||||
/* float pol_theta = Vector2Angle(Vec2Df{1,0},(mouse_pos - closest->position));*/
|
||||
/**/
|
||||
/* Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta); */
|
||||
/* target_position = closest->position + polar;*/
|
||||
/* } else {*/
|
||||
/* target_position = mouse_pos;*/
|
||||
/* }*/
|
||||
/**/
|
||||
/* if(connecting_nodes.front()->connecting && mouse_down){*/
|
||||
/* DrawLine(connector_position.x, connector_position.y, target_position.x, target_position.y, (Color){229, 181, 103, 255});*/
|
||||
/* connecting_nodes.front()->hover_connect = true;*/
|
||||
/* }*/
|
||||
/**/
|
||||
/* if(!hovering_connectors.empty()) {*/
|
||||
/* hovering_connectors.front()->hover_connect = true;*/
|
||||
/* }*/
|
||||
/**/
|
||||
/* if(mouse_released) {*/
|
||||
/* if(&hovering_nodes.front() != &connecting_nodes.front() && &connecting_nodes.front() != &hovering_connectors.front()){*/
|
||||
/* if(!hovering_nodes.empty()){*/
|
||||
/* app.gm.addConnection(connecting_nodes.front(), hovering_nodes.front());*/
|
||||
/* } else if(!hovering_connectors.empty()){*/
|
||||
/* app.gm.addConnection(connecting_nodes.front(), hovering_connectors.front());*/
|
||||
/* } else {*/
|
||||
/* SPTR_Node new_node = app.gm.copyNode(std::dynamic_pointer_cast<MidiNode>(connecting_nodes.front()), target_position);*/
|
||||
/* new_node->connecting = false;*/
|
||||
/* app.gm.addConnection(connecting_nodes.front(), new_node);*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/* connecting_nodes.front()->connecting = false;*/
|
||||
/* istate = InteractionState::HOVER_CANVAS;*/
|
||||
/* }*/
|
||||
/* break;*/
|
||||
/* }*/
|
||||
/* case InteractionState::DELETE_SELECTED_NODES:*/
|
||||
/* {*/
|
||||
/* app.removeNodes(selected_nodes | std::ranges::to<VEC_SPTR_Node>());*/
|
||||
/* break;*/
|
||||
/* }*/
|
||||
/* case InteractionState::COPY_SELECTED_NODES:*/
|
||||
/* {*/
|
||||
/* std::ranges::copy(selected_nodes.begin(), selected_nodes.end(), std::back_inserter(copied_nodes));*/
|
||||
/* std::ranges::for_each(selected_nodes.begin(), selected_nodes.end(), [](SPTR_Node n){n->selected = false;});*/
|
||||
/* selected_nodes.clear();*/
|
||||
/* break;*/
|
||||
/* }*/
|
||||
/* case InteractionState::PASTE_SELECTED_NODES:*/
|
||||
/* {*/
|
||||
/* for(auto n: copied_nodes){*/
|
||||
/* n->drag_offset = (n->position - copied_nodes.front()->position);*/
|
||||
/* }*/
|
||||
/* app.copyNodesAndConnections(copied_nodes, mouse_pos);*/
|
||||
/* //copied_nodes.clear();*/
|
||||
/* break;*/
|
||||
/* }*/
|
||||
/*};*/
|
||||
|
||||
}
|
||||
|
||||
@@ -82,6 +82,10 @@ namespace VISEQ::GRAPH::NODES {
|
||||
int GGate(){
|
||||
return midi_gate;
|
||||
}
|
||||
void update() override {
|
||||
Node::update();
|
||||
mode = state->node_mode;
|
||||
}
|
||||
|
||||
void interact(){
|
||||
float wheel = GetMouseWheelMove();
|
||||
|
||||
@@ -18,12 +18,16 @@ namespace VISEQ::SIGNAL {
|
||||
typedef std::shared_ptr<Signal> SPTR_Signal;
|
||||
typedef std::vector<SPTR_Signal> VEC_SPTR_Signal;
|
||||
|
||||
class SignalManager{
|
||||
class SignalManager : public std::enable_shared_from_this<SignalManager>{
|
||||
|
||||
public:
|
||||
SignalManager() {}
|
||||
SignalManager(GraphManager *gm, std::mutex *mutex) : mutex(mutex), gm(gm) {}
|
||||
|
||||
std::shared_ptr<SignalManager> getPtr(){
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
VISEQ::MIDI::Midi midi;
|
||||
|
||||
std::mutex *mutex;
|
||||
|
||||
@@ -78,6 +78,7 @@ namespace VISEQ::APP{
|
||||
ModifierKeys keys_released;
|
||||
|
||||
Rectangle selection;
|
||||
Rectangle bounding_box;
|
||||
|
||||
Vec2Df mouse_delta;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user