way too many people, should not have added in the first place)tags/v0.3.2
@@ -57,6 +57,8 @@ struct ModuleWidget : OpaqueWidget { | |||
bool requested = false; | |||
Vec requestedPos; | |||
Vec dragPos; | |||
Widget *onMouseMove(Vec pos, Vec mouseRel); | |||
Widget *onHoverKey(Vec pos, int key); | |||
void onDragStart(); | |||
void onDragMove(Vec mouseRel); | |||
void onDragEnd(); | |||
@@ -97,7 +99,12 @@ struct RackWidget : OpaqueWidget { | |||
json_t *toJson(); | |||
void fromJson(json_t *root); | |||
void repositionModule(ModuleWidget *module); | |||
void addModule(ModuleWidget *m); | |||
/** Transfers ownership to the caller so they must `delete` it if that is the intension */ | |||
void deleteModule(ModuleWidget *m); | |||
void cloneModule(ModuleWidget *m); | |||
/** Moves a module to the closest non-colliding position */ | |||
void repositionModule(ModuleWidget *m); | |||
void step(); | |||
void draw(NVGcontext *vg); | |||
@@ -18,13 +18,6 @@ void guiDestroy(); | |||
void guiRun(); | |||
void guiCursorLock(); | |||
void guiCursorUnlock(); | |||
inline bool guiIsModPressed() { | |||
#ifdef ARCH_MAC | |||
return glfwGetKey(gWindow, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS; | |||
#else | |||
return glfwGetKey(gWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS; | |||
#endif | |||
} | |||
bool guiIsModPressed(); | |||
} // namespace rack |
@@ -91,15 +91,17 @@ struct Widget { | |||
*/ | |||
virtual Widget *onMouseDown(Vec pos, int button); | |||
virtual Widget *onMouseUp(Vec pos, int button); | |||
/** Called on every frame, even if mouseRel = Vec(0, 0) */ | |||
virtual Widget *onMouseMove(Vec pos, Vec mouseRel); | |||
virtual Widget *onHoverKey(Vec pos, int key); | |||
/** Called when this widget begins responding to `onMouseMove` events */ | |||
virtual void onMouseEnter() {} | |||
/** Called when another widget begins responding to `onMouseMove` events */ | |||
virtual void onMouseLeave() {} | |||
virtual void onSelect() {} | |||
virtual void onDeselect() {} | |||
virtual void onText(int codepoint) {} | |||
virtual void onKey(int key) {} | |||
virtual bool onText(int codepoint) {return false;} | |||
virtual bool onKey(int key) {return false;} | |||
virtual Widget *onScroll(Vec pos, Vec scrollRel); | |||
/** Called when a widget responds to `onMouseDown` for a left button press */ | |||
@@ -364,8 +366,8 @@ struct TextField : OpaqueWidget { | |||
} | |||
void draw(NVGcontext *vg); | |||
Widget *onMouseDown(Vec pos, int button); | |||
void onText(int codepoint); | |||
void onKey(int scancode); | |||
bool onText(int codepoint); | |||
bool onKey(int scancode); | |||
void onSelect(); | |||
void insertText(std::string newText); | |||
}; | |||
@@ -1,6 +1,7 @@ | |||
#include "app.hpp" | |||
#include "engine.hpp" | |||
#include "plugin.hpp" | |||
#include "gui.hpp" | |||
namespace rack { | |||
@@ -125,6 +126,7 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||
Widget::draw(vg); | |||
/* | |||
// CPU usage text | |||
if (dynamic_cast<RackScene*>(gScene)->toolbar->cpuUsageButton->value > 0.0) { | |||
float cpuTime = module ? module->cpuTime : 0.0; | |||
@@ -145,10 +147,38 @@ void ModuleWidget::draw(NVGcontext *vg) { | |||
bndMenuItem(vg, 0.0, 0.0, box.size.x, BND_WIDGET_HEIGHT, BND_DEFAULT, -1, text.c_str()); | |||
nvgRestore(vg); | |||
} | |||
*/ | |||
nvgResetScissor(vg); | |||
} | |||
Widget *ModuleWidget::onMouseMove(Vec pos, Vec mouseRel) { | |||
return OpaqueWidget::onMouseMove(pos, mouseRel); | |||
} | |||
Widget *ModuleWidget::onHoverKey(Vec pos, int key) { | |||
switch (key) { | |||
case GLFW_KEY_DELETE: | |||
case GLFW_KEY_BACKSPACE: | |||
gRackWidget->deleteModule(this); | |||
delete this; | |||
break; | |||
case GLFW_KEY_I: | |||
if (guiIsModPressed()) | |||
initialize(); | |||
break; | |||
case GLFW_KEY_R: | |||
if (guiIsModPressed()) | |||
randomize(); | |||
break; | |||
case GLFW_KEY_D: | |||
if (guiIsModPressed()) | |||
gRackWidget->cloneModule(this); | |||
break; | |||
} | |||
return NULL; | |||
} | |||
void ModuleWidget::onDragStart() { | |||
dragPos = gMousePos.minus(getAbsolutePos()); | |||
} | |||
@@ -185,22 +215,14 @@ struct RandomizeMenuItem : MenuItem { | |||
struct CloneMenuItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction() { | |||
// Create new module from model | |||
ModuleWidget *clonedModuleWidget = moduleWidget->model->createModuleWidget(); | |||
// JSON serialization is the most straightforward way to do this | |||
json_t *moduleJ = moduleWidget->toJson(); | |||
clonedModuleWidget->fromJson(moduleJ); | |||
json_decref(moduleJ); | |||
clonedModuleWidget->requestedPos = moduleWidget->box.pos; | |||
clonedModuleWidget->requested = true; | |||
gRackWidget->moduleContainer->addChild(clonedModuleWidget); | |||
gRackWidget->cloneModule(moduleWidget); | |||
} | |||
}; | |||
struct DeleteMenuItem : MenuItem { | |||
ModuleWidget *moduleWidget; | |||
void onAction() { | |||
gRackWidget->moduleContainer->removeChild(moduleWidget); | |||
gRackWidget->deleteModule(moduleWidget); | |||
delete moduleWidget; | |||
} | |||
}; | |||
@@ -229,7 +251,7 @@ void ModuleWidget::onMouseDown(int button) { | |||
menu->pushChild(disconnectItem); | |||
CloneMenuItem *cloneItem = new CloneMenuItem(); | |||
cloneItem->text = "Clone"; | |||
cloneItem->text = "Duplicate"; | |||
cloneItem->moduleWidget = this; | |||
menu->pushChild(cloneItem); | |||
@@ -226,29 +226,49 @@ void RackWidget::fromJson(json_t *rootJ) { | |||
} | |||
} | |||
void RackWidget::repositionModule(ModuleWidget *module) { | |||
void RackWidget::addModule(ModuleWidget *m) { | |||
moduleContainer->addChild(m); | |||
} | |||
void RackWidget::deleteModule(ModuleWidget *m) { | |||
moduleContainer->removeChild(m); | |||
} | |||
void RackWidget::cloneModule(ModuleWidget *m) { | |||
// Create new module from model | |||
ModuleWidget *clonedModuleWidget = m->model->createModuleWidget(); | |||
// JSON serialization is the most straightforward way to do this | |||
json_t *moduleJ = m->toJson(); | |||
clonedModuleWidget->fromJson(moduleJ); | |||
json_decref(moduleJ); | |||
clonedModuleWidget->requestedPos = m->box.pos; | |||
clonedModuleWidget->requested = true; | |||
addModule(clonedModuleWidget); | |||
} | |||
void RackWidget::repositionModule(ModuleWidget *m) { | |||
// Create possible positions | |||
int x0 = roundf(module->requestedPos.x / RACK_GRID_WIDTH); | |||
int y0 = roundf(module->requestedPos.y / RACK_GRID_HEIGHT); | |||
int x0 = roundf(m->requestedPos.x / RACK_GRID_WIDTH); | |||
int y0 = roundf(m->requestedPos.y / RACK_GRID_HEIGHT); | |||
std::vector<Vec> positions; | |||
for (int y = maxi(0, y0 - 2); y < y0 + 2; y++) { | |||
for (int x = maxi(0, x0 - 40); x < x0 + 40; x++) { | |||
for (int y = maxi(0, y0 - 4); y < y0 + 4; y++) { | |||
for (int x = maxi(0, x0 - 200); x < x0 + 200; x++) { | |||
positions.push_back(Vec(x * RACK_GRID_WIDTH, y * RACK_GRID_HEIGHT)); | |||
} | |||
} | |||
// Sort possible positions by distance to the requested position | |||
Vec requestedPos = module->requestedPos; | |||
Vec requestedPos = m->requestedPos; | |||
std::sort(positions.begin(), positions.end(), [requestedPos](Vec a, Vec b) { | |||
return a.minus(requestedPos).norm() < b.minus(requestedPos).norm(); | |||
}); | |||
// Find a position that does not collide | |||
for (Vec pos : positions) { | |||
Rect newBox = Rect(pos, module->box.size); | |||
Rect newBox = Rect(pos, m->box.size); | |||
bool collides = false; | |||
for (Widget *child2 : moduleContainer->children) { | |||
if (module == child2) continue; | |||
if (m == child2) continue; | |||
if (newBox.intersects(child2->box)) { | |||
collides = true; | |||
break; | |||
@@ -256,7 +276,7 @@ void RackWidget::repositionModule(ModuleWidget *module) { | |||
} | |||
if (collides) continue; | |||
module->box.pos = pos; | |||
m->box.pos = pos; | |||
break; | |||
} | |||
} | |||
@@ -139,6 +139,7 @@ Toolbar::Toolbar() { | |||
xPos += wireTensionSlider->box.size.x; | |||
} | |||
/* | |||
xPos += margin; | |||
{ | |||
cpuUsageButton = new RadioButton(); | |||
@@ -148,6 +149,7 @@ Toolbar::Toolbar() { | |||
addChild(cpuUsageButton); | |||
xPos += cpuUsageButton->box.size.x; | |||
} | |||
*/ | |||
xPos += margin; | |||
{ | |||
@@ -157,34 +157,13 @@ void charCallback(GLFWwindow *window, unsigned int codepoint) { | |||
} | |||
} | |||
// static int lastWindowX, lastWindowY, lastWindowWidth, lastWindowHeight; | |||
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { | |||
if (action == GLFW_PRESS || action == GLFW_REPEAT) { | |||
if (key == GLFW_KEY_F11 || key == GLFW_KEY_ESCAPE) { | |||
/* | |||
// Toggle fullscreen | |||
GLFWmonitor *monitor = glfwGetWindowMonitor(gWindow); | |||
if (monitor) { | |||
// Window mode | |||
glfwSetWindowMonitor(gWindow, NULL, lastWindowX, lastWindowY, lastWindowWidth, lastWindowHeight, 0); | |||
} | |||
else { | |||
// Fullscreen | |||
glfwGetWindowPos(gWindow, &lastWindowX, &lastWindowY); | |||
glfwGetWindowSize(gWindow, &lastWindowWidth, &lastWindowHeight); | |||
monitor = glfwGetPrimaryMonitor(); | |||
assert(monitor); | |||
const GLFWvidmode *mode = glfwGetVideoMode(monitor); | |||
glfwSetWindowMonitor(gWindow, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); | |||
} | |||
*/ | |||
} | |||
else { | |||
if (gSelectedWidget) { | |||
gSelectedWidget->onKey(key); | |||
} | |||
} | |||
// onKey | |||
if (gSelectedWidget && gSelectedWidget->onKey(key)) | |||
return; | |||
// onHoverKey | |||
gScene->onHoverKey(gMousePos, key); | |||
} | |||
} | |||
@@ -228,6 +207,9 @@ void guiInit() { | |||
err = glfwInit(); | |||
assert(err); | |||
const char *glVersion = (const char *)glGetString(GL_VERSION); | |||
printf("OpenGL version %s\n", glVersion); | |||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); | |||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); | |||
// glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); | |||
@@ -329,6 +311,14 @@ void guiCursorUnlock() { | |||
glfwSetInputMode(gWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); | |||
} | |||
bool guiIsModPressed() { | |||
#ifdef ARCH_MAC | |||
return glfwGetKey(gWindow, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS; | |||
#else | |||
return glfwGetKey(gWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(gWindow, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS; | |||
#endif | |||
} | |||
//////////////////// | |||
// resources | |||
@@ -29,13 +29,13 @@ Widget *TextField::onMouseDown(Vec pos, int button) { | |||
} | |||
void TextField::onText(int codepoint) { | |||
bool TextField::onText(int codepoint) { | |||
char c = codepoint; | |||
std::string newText(1, c); | |||
insertText(newText); | |||
} | |||
void TextField::onKey(int key) { | |||
bool TextField::onKey(int key) { | |||
switch (key) { | |||
case GLFW_KEY_BACKSPACE: | |||
if (begin < end) { | |||
@@ -121,6 +121,20 @@ Widget *Widget::onMouseMove(Vec pos, Vec mouseRel) { | |||
return NULL; | |||
} | |||
Widget *Widget::onHoverKey(Vec pos, int key) { | |||
for (auto it = children.rbegin(); it != children.rend(); it++) { | |||
Widget *child = *it; | |||
if (!child->visible) | |||
continue; | |||
if (child->box.contains(pos)) { | |||
Widget *w = child->onHoverKey(pos.minus(child->box.pos), key); | |||
if (w) | |||
return w; | |||
} | |||
} | |||
return NULL; | |||
} | |||
Widget *Widget::onScroll(Vec pos, Vec scrollRel) { | |||
for (auto it = children.rbegin(); it != children.rend(); it++) { | |||
Widget *child = *it; | |||