Browse Source

Add scrolling to MIDI-Map with up to 128 mappings. Add onAdd and onRemove events. Fix minor ScrollWidget behavior.

tags/v1.0.0
Andrew Belt 5 years ago
parent
commit
578dd8aed2
14 changed files with 206 additions and 135 deletions
  1. +2
    -2
      include/app/LedDisplay.hpp
  2. +19
    -7
      include/event.hpp
  3. +2
    -2
      include/ui/ScrollWidget.hpp
  4. +1
    -1
      include/ui/Slider.hpp
  5. +0
    -10
      include/widget/OpaqueWidget.hpp
  6. +2
    -0
      include/widget/Widget.hpp
  7. +143
    -84
      src/Core/MIDI_Map.cpp
  8. +3
    -4
      src/app/LedDisplay.cpp
  9. +1
    -0
      src/engine/Engine.cpp
  10. +0
    -18
      src/event.cpp
  11. +9
    -1
      src/ui/ScrollWidget.cpp
  12. +3
    -6
      src/ui/Slider.cpp
  13. +16
    -0
      src/widget/Widget.cpp
  14. +5
    -0
      src/window.cpp

+ 2
- 2
include/app/LedDisplay.hpp View File

@@ -1,6 +1,6 @@
#pragma once
#include "app/common.hpp"
#include "widget/Widget.hpp"
#include "widget/OpaqueWidget.hpp"
#include "widget/TransparentWidget.hpp"
#include "ui/TextField.hpp"

@@ -9,7 +9,7 @@ namespace rack {
namespace app {


struct LedDisplay : widget::Widget {
struct LedDisplay : widget::OpaqueWidget {
void draw(const widget::DrawContext &ctx) override;
};



+ 19
- 7
include/event.hpp View File

@@ -222,37 +222,51 @@ struct PathDrop : Event, Position {
};


/** Occurs when an certain action is triggered on a Widget.
/** Occurs after a certain action is triggered on a Widget.
The concept of an "action" is dependent on the type of Widget.
*/
struct Action : Event {
};


/** Occurs when the value of a Widget changes.
/** Occurs after the value of a Widget changes.
The concept of a "value" is dependent on the type of Widget.
*/
struct Change : Event {
};


/** Occurs when the zoom level of a Widget is changed.
/** Occurs after the zoom level of a Widget is changed.
Recurses until consumed.
*/
struct Zoom : Event {
};


/** Occurs when Widget::setPos() is called.
/** Occurs after the Widget's position is set by setPos();
*/
struct Reposition : Event {
};


/** Occurs when Widget::setSize() is called.
/** Occurs after the Widget's size is set by setSize();
*/
struct Resize : Event {
};


/** Occurs after the Widget is added to a parent.
*/
struct Add : Event {
};


/** Occurs before the Widget is remove from its parent.
*/
struct Remove : Event {
};


struct State {
widget::Widget *rootWidget = NULL;
/** State widgets
@@ -262,8 +276,6 @@ struct State {
widget::Widget *draggedWidget = NULL;
widget::Widget *dragHoveredWidget = NULL;
widget::Widget *selectedWidget = NULL;
/** For middle-click dragging */
widget::Widget *scrollWidget = NULL;
/** For double-clicking */
double lastClickTime = -INFINITY;
widget::Widget *lastClickedWidget = NULL;


+ 2
- 2
include/ui/ScrollWidget.hpp View File

@@ -1,6 +1,6 @@
#pragma once
#include "ui/common.hpp"
#include "widget/OpaqueWidget.hpp"
#include "widget/Widget.hpp"
#include "ui/ScrollBar.hpp"


@@ -9,7 +9,7 @@ namespace ui {


/** Handles a container with ScrollBar */
struct ScrollWidget : widget::OpaqueWidget {
struct ScrollWidget : widget::Widget {
widget::Widget *container;
ScrollBar *horizontalScrollBar;
ScrollBar *verticalScrollBar;


+ 1
- 1
include/ui/Slider.hpp View File

@@ -19,7 +19,7 @@ struct Slider : widget::OpaqueWidget {
void onDragStart(const event::DragStart &e) override;
void onDragMove(const event::DragMove &e) override;
void onDragEnd(const event::DragEnd &e) override;
void onButton(const event::Button &e) override;
void onDoubleClick(const event::DoubleClick &e) override;
};




+ 0
- 10
include/widget/OpaqueWidget.hpp View File

@@ -31,21 +31,11 @@ struct OpaqueWidget : Widget {
if (!e.getConsumed())
e.consume(this);
}
void onHoverScroll(const event::HoverScroll &e) override {
Widget::onHoverScroll(e);
if (!e.getConsumed())
e.consume(this);
}
void onDragHover(const event::DragHover &e) override {
Widget::onDragHover(e);
if (!e.getConsumed())
e.consume(this);
}
void onPathDrop(const event::PathDrop &e) override {
Widget::onPathDrop(e);
if (!e.getConsumed())
e.consume(this);
}
};




+ 2
- 0
include/widget/Widget.hpp View File

@@ -148,6 +148,8 @@ struct Widget {
virtual void onZoom(const event::Zoom &e) {recurseEvent(&Widget::onZoom, e);}
virtual void onReposition(const event::Reposition &e) {}
virtual void onResize(const event::Resize &e) {}
virtual void onAdd(const event::Add &e) {}
virtual void onRemove(const event::Remove &e) {}
};




+ 143
- 84
src/Core/MIDI_Map.cpp View File

@@ -1,7 +1,7 @@
#include "plugin.hpp"


static const int CHANNELS = 8;
static const int MAX_CHANNELS = 128;


struct MIDI_Map : Module {
@@ -19,33 +19,37 @@ struct MIDI_Map : Module {
};

midi::InputQueue midiInput;

/** Number of maps */
int mapLen = 0;
/** The mapped CC number of each channel */
int ccs[MAX_CHANNELS];
/** The mapped param handle of each channel */
ParamHandle paramHandles[MAX_CHANNELS];

/** Channel ID of the learning session */
int learningId;
/** Whether the CC has been set during the learning session */
bool learnedCc;
/** Whether the param has been set during the learning session */
bool learnedParam;
/** The learned CC number of each channel */
int learnedCcs[CHANNELS];
/** The learned param handle of each channel */
ParamHandle learnedParamHandles[CHANNELS];

/** The value of each CC number */
int8_t values[128];
/** The smoothing processor (normalized between 0 and 1) of each channel */
dsp::ExponentialFilter valueFilters[CHANNELS];
dsp::ExponentialFilter valueFilters[MAX_CHANNELS];

MIDI_Map() {
config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
for (int i = 0; i < CHANNELS; i++) {
APP->engine->addParamHandle(&learnedParamHandles[i]);
valueFilters[i].lambda = 60.f;
for (int id = 0; id < MAX_CHANNELS; id++) {
APP->engine->addParamHandle(&paramHandles[id]);
}
onReset();
}

~MIDI_Map() {
for (int i = 0; i < CHANNELS; i++) {
APP->engine->removeParamHandle(&learnedParamHandles[i]);
for (int id = 0; id < MAX_CHANNELS; id++) {
APP->engine->removeParamHandle(&paramHandles[id]);
}
}

@@ -53,11 +57,8 @@ struct MIDI_Map : Module {
learningId = -1;
learnedCc = false;
learnedParam = false;
for (int i = 0; i < CHANNELS; i++) {
learnedCcs[i] = -1;
APP->engine->updateParamHandle(&learnedParamHandles[i], -1, 0);
valueFilters[i].reset();
}
clearMaps();
mapLen = 1;
for (int i = 0; i < 128; i++) {
values[i] = -1;
}
@@ -73,20 +74,19 @@ struct MIDI_Map : Module {
float deltaTime = APP->engine->getSampleTime();

// Step channels
for (int id = 0; id < CHANNELS; id++) {
int cc = learnedCcs[id];
for (int id = 0; id < mapLen; id++) {
int cc = ccs[id];
if (cc < 0)
continue;
// DEBUG("%d %d %p %d", id, learnedCcs[id], learnedParamHandles[id].module, learnedParamHandles[id].paramId);
// Check if CC value has been set
if (values[cc] < 0)
continue;
// Get module
Module *module = learnedParamHandles[id].module;
Module *module = paramHandles[id].module;
if (!module)
continue;
// Get param
int paramId = learnedParamHandles[id].paramId;
int paramId = paramHandles[id].paramId;
Param *param = &module->params[paramId];
if (!param->isBounded())
continue;
@@ -110,14 +110,47 @@ struct MIDI_Map : Module {

void processCC(midi::Message msg) {
uint8_t cc = msg.getNote();
int8_t value = msg.getValue();
// Learn
if (learningId >= 0 && values[cc] != msg.getValue()) {
learnedCcs[learningId] = cc;
if (0 <= learningId && values[cc] != value) {
ccs[learningId] = cc;
valueFilters[learningId].reset();
learnedCc = true;
commitLearn();
updateMapLen();
}
values[cc] = msg.getValue();
values[cc] = value;
}

void clearMap(int id) {
learningId = -1;
ccs[id] = -1;
APP->engine->updateParamHandle(&paramHandles[id], -1, 0, true);
valueFilters[id].reset();
updateMapLen();
}

void clearMaps() {
learningId = -1;
for (int id = 0; id < MAX_CHANNELS; id++) {
ccs[id] = -1;
APP->engine->updateParamHandle(&paramHandles[id], -1, 0, true);
valueFilters[id].reset();
}
mapLen = 0;
}

void updateMapLen() {
// Find last nonempty map
int id;
for (id = MAX_CHANNELS - 1; id >= 0; id--) {
if (ccs[id] >= 0 || paramHandles[id].moduleId >= 0)
break;
}
mapLen = id + 1;
// Add an empty "Mapping..." slot
if (mapLen < MAX_CHANNELS)
mapLen++;
}

void commitLearn() {
@@ -130,20 +163,14 @@ struct MIDI_Map : Module {
// Reset learned state
learnedCc = false;
learnedParam = false;
// Find next unlearned channel
while (++learningId < CHANNELS) {
if (learnedCcs[learningId] < 0 || learnedParamHandles[learningId].moduleId < 0)
// Find next incomplete map
while (++learningId < MAX_CHANNELS) {
if (ccs[learningId] < 0 || paramHandles[learningId].moduleId < 0)
return;
}
learningId = -1;
}

void clearLearn(int id) {
disableLearn(id);
learnedCcs[id] = -1;
APP->engine->updateParamHandle(&learnedParamHandles[id], -1, 0);
}

void enableLearn(int id) {
if (learningId != id) {
learningId = id;
@@ -159,53 +186,50 @@ struct MIDI_Map : Module {
}

void learnParam(int id, int moduleId, int paramId) {
APP->engine->updateParamHandle(&learnedParamHandles[id], moduleId, paramId, true);
APP->engine->updateParamHandle(&paramHandles[id], moduleId, paramId, true);
learnedParam = true;
commitLearn();
updateMapLen();
}

json_t *dataToJson() override {
json_t *rootJ = json_object();

json_t *ccsJ = json_array();
for (int i = 0; i < 8; i++) {
json_array_append_new(ccsJ, json_integer(learnedCcs[i]));
}
json_object_set_new(rootJ, "ccs", ccsJ);

json_t *moduleIdsJ = json_array();
json_t *paramIdsJ = json_array();
for (int i = 0; i < CHANNELS; i++) {
json_array_append_new(moduleIdsJ, json_integer(learnedParamHandles[i].moduleId));
json_array_append_new(paramIdsJ, json_integer(learnedParamHandles[i].paramId));
json_t *mapsJ = json_array();
for (int id = 0; id < mapLen; id++) {
json_t *mapJ = json_object();
json_object_set_new(mapJ, "cc", json_integer(ccs[id]));
json_object_set_new(mapJ, "moduleId", json_integer(paramHandles[id].moduleId));
json_object_set_new(mapJ, "paramId", json_integer(paramHandles[id].paramId));
json_array_append(mapsJ, mapJ);
}
json_object_set_new(rootJ, "moduleIds", moduleIdsJ);
json_object_set_new(rootJ, "paramIds", paramIdsJ);
json_object_set_new(rootJ, "maps", mapsJ);

json_object_set_new(rootJ, "midi", midiInput.toJson());
return rootJ;
}

void dataFromJson(json_t *rootJ) override {
json_t *ccsJ = json_object_get(rootJ, "ccs");
if (ccsJ) {
for (int i = 0; i < CHANNELS; i++) {
json_t *ccJ = json_array_get(ccsJ, i);
if (ccJ)
learnedCcs[i] = json_integer_value(ccJ);
clearMaps();

json_t *mapsJ = json_object_get(rootJ, "maps");
if (mapsJ) {
json_t *mapJ;
size_t mapIndex;
json_array_foreach(mapsJ, mapIndex, mapJ) {
json_t *ccJ = json_object_get(mapJ, "cc");
json_t *moduleIdJ = json_object_get(mapJ, "moduleId");
json_t *paramIdJ = json_object_get(mapJ, "paramId");
if (!(ccJ && moduleIdJ && paramIdJ))
continue;
if (mapIndex >= MAX_CHANNELS)
continue;
ccs[mapIndex] = json_integer_value(ccJ);
APP->engine->updateParamHandle(&paramHandles[mapIndex], json_integer_value(moduleIdJ), json_integer_value(paramIdJ), false);
}
}

json_t *moduleIdsJ = json_object_get(rootJ, "moduleIds");
json_t *paramIdsJ = json_object_get(rootJ, "paramIds");
if (moduleIdsJ && paramIdsJ) {
for (int i = 0; i < CHANNELS; i++) {
json_t *moduleIdJ = json_array_get(moduleIdsJ, i);
json_t *paramIdJ = json_array_get(paramIdsJ, i);
if (moduleIdJ && paramIdsJ)
APP->engine->updateParamHandle(&learnedParamHandles[i], json_integer_value(moduleIdJ), json_integer_value(paramIdJ), false);
}
}
updateMapLen();

json_t *midiJ = json_object_get(rootJ, "midi");
if (midiJ)
@@ -232,7 +256,7 @@ struct MIDI_MapChoice : LedDisplayChoice {
}

if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
module->clearLearn(id);
module->clearMap(id);
e.consume(this);
}
}
@@ -240,6 +264,10 @@ struct MIDI_MapChoice : LedDisplayChoice {
void onSelect(const event::Select &e) override {
if (!module)
return;

ScrollWidget *scroll = getAncestorOfType<ScrollWidget>();
scroll->scrollTo(box);

// Reset touchedParam
APP->scene->rackWidget->touchedParam = NULL;
module->enableLearn(id);
@@ -285,13 +313,13 @@ struct MIDI_MapChoice : LedDisplayChoice {

// Set text
text = "";
if (module->learnedCcs[id] >= 0) {
text += string::f("CC%d ", module->learnedCcs[id]);
if (module->ccs[id] >= 0) {
text += string::f("CC%d ", module->ccs[id]);
}
if (module->learnedParamHandles[id].moduleId >= 0) {
if (module->paramHandles[id].moduleId >= 0) {
text += getParamName();
}
if (module->learnedCcs[id] < 0 && module->learnedParamHandles[id].moduleId < 0) {
if (module->ccs[id] < 0 && module->paramHandles[id].moduleId < 0) {
if (module->learningId == id) {
text = "Mapping...";
}
@@ -301,7 +329,7 @@ struct MIDI_MapChoice : LedDisplayChoice {
}

// Set text color
if ((module->learnedCcs[id] >= 0 && module->learnedParamHandles[id].moduleId >= 0) || module->learningId == id) {
if ((module->ccs[id] >= 0 && module->paramHandles[id].moduleId >= 0) || module->learningId == id) {
color.a = 1.0;
}
else {
@@ -312,7 +340,9 @@ struct MIDI_MapChoice : LedDisplayChoice {
std::string getParamName() {
if (!module)
return "";
ParamHandle *paramHandle = &module->learnedParamHandles[id];
if (id >= module->mapLen)
return "";
ParamHandle *paramHandle = &module->paramHandles[id];
if (paramHandle->moduleId < 0)
return "";
ModuleWidget *mw = APP->scene->rackWidget->getModule(paramHandle->moduleId);
@@ -337,28 +367,57 @@ struct MIDI_MapChoice : LedDisplayChoice {


struct MIDI_MapDisplay : MidiWidget {
void setModule(MIDI_Map *module) {
// ScrollWidget *scroll = new ScrollWidget;
// scroll->box.pos = channelChoice->box.getBottomLeft();
// scroll->box.size.x = box.size.x;
// scroll->box.size.y = box.size.y - scroll->box.pos.y;
// addChild(scroll);
MIDI_Map *module;
ScrollWidget *scroll;
MIDI_MapChoice *choices[MAX_CHANNELS];
LedDisplaySeparator *separators[MAX_CHANNELS];

Vec pos = channelChoice->box.getBottomLeft();
void setModule(MIDI_Map *module) {
this->module = module;

for (int i = 0; i < CHANNELS; i++) {
LedDisplaySeparator *separator = createWidget<LedDisplaySeparator>(pos);
separator->box.size.x = box.size.x;
addChild(separator);
scroll = new ScrollWidget;
scroll->box.pos = channelChoice->box.getBottomLeft();
scroll->box.size.x = box.size.x;
scroll->box.size.y = box.size.y - scroll->box.pos.y;
addChild(scroll);

LedDisplaySeparator *separator = createWidget<LedDisplaySeparator>(scroll->box.pos);
separator->box.size.x = box.size.x;
addChild(separator);
separators[0] = separator;

Vec pos;
for (int id = 0; id < MAX_CHANNELS; id++) {
if (id > 0) {
LedDisplaySeparator *separator = createWidget<LedDisplaySeparator>(pos);
separator->box.size.x = box.size.x;
scroll->container->addChild(separator);
separators[id] = separator;
}

MIDI_MapChoice *choice = createWidget<MIDI_MapChoice>(pos);
choice->box.size.x = box.size.x;
choice->id = i;
choice->id = id;
choice->setModule(module);
addChild(choice);
scroll->container->addChild(choice);
choices[id] = choice;

pos = choice->box.getBottomLeft();
}
}

void step() override {
if (!module)
return;

int mapLen = module->mapLen;
for (int id = 0; id < MAX_CHANNELS; id++) {
choices[id]->visible = (id < mapLen);
separators[id]->visible = (id < mapLen);
}

MidiWidget::step();
}
};


@@ -372,7 +431,7 @@ struct MIDI_MapWidget : ModuleWidget {
addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));

MIDI_MapDisplay *midiWidget = createWidget<MIDI_MapDisplay>(mm2px(Vec(3.4, 14.839)));
MIDI_MapDisplay *midiWidget = createWidget<MIDI_MapDisplay>(mm2px(Vec(3.41891, 14.8373)));
midiWidget->box.size = mm2px(Vec(43.999, 102.664));
midiWidget->setMidiIO(module ? &module->midiInput : NULL);
midiWidget->setModule(module);


+ 3
- 4
src/app/LedDisplay.cpp View File

@@ -10,12 +10,15 @@ namespace app {


void LedDisplay::draw(const widget::DrawContext &ctx) {

nvgBeginPath(ctx.vg);
nvgRoundedRect(ctx.vg, 0, 0, box.size.x, box.size.y, 5.0);
nvgFillColor(ctx.vg, nvgRGB(0x00, 0x00, 0x00));
nvgFill(ctx.vg);

nvgScissor(ctx.vg, 0, 0, box.size.x, box.size.y);
widget::Widget::draw(ctx);
nvgResetScissor(ctx.vg);
}


@@ -42,8 +45,6 @@ LedDisplayChoice::LedDisplayChoice() {
}

void LedDisplayChoice::draw(const widget::DrawContext &ctx) {
nvgScissor(ctx.vg, 0, 0, box.size.x, box.size.y);

if (bgColor.a > 0.0) {
nvgBeginPath(ctx.vg);
nvgRect(ctx.vg, 0, 0, box.size.x, box.size.y);
@@ -59,8 +60,6 @@ void LedDisplayChoice::draw(const widget::DrawContext &ctx) {
nvgFontSize(ctx.vg, 12);
nvgText(ctx.vg, textOffset.x, textOffset.y, text.c_str(), NULL);
}

nvgResetScissor(ctx.vg);
}

void LedDisplayChoice::onButton(const event::Button &e) {


+ 1
- 0
src/engine/Engine.cpp View File

@@ -589,6 +589,7 @@ void Engine::addParamHandle(ParamHandle *paramHandle) {
auto it = std::find(internal->paramHandles.begin(), internal->paramHandles.end(), paramHandle);
assert(it == internal->paramHandles.end());

// New ParamHandles must be blank
assert(paramHandle->moduleId < 0);
internal->paramHandles.push_back(paramHandle);
}


+ 0
- 18
src/event.cpp View File

@@ -97,7 +97,6 @@ void State::finalizeWidget(widget::Widget *w) {
if (draggedWidget == w) setDragged(NULL);
if (dragHoveredWidget == w) setDragHovered(NULL);
if (selectedWidget == w) setSelected(NULL);
if (scrollWidget == w) scrollWidget = NULL;
if (lastClickedWidget == w) lastClickedWidget = NULL;
}

@@ -149,15 +148,6 @@ void State::handleButton(math::Vec pos, int button, int action, int mods) {
lastClickedWidget = clickedWidget;
}
}

// if (button == GLFW_MOUSE_BUTTON_MIDDLE) {
// if (action == GLFW_PRESS) {
// scrollWidget = clickedWidget;
// }
// if (action == GLFW_RELEASE) {
// scrollWidget = NULL;
// }
// }
}

void State::handleHover(math::Vec pos, math::Vec mouseDelta) {
@@ -181,14 +171,6 @@ void State::handleHover(math::Vec pos, math::Vec mouseDelta) {
return;
}

// if (scrollWidget) {
// event::HoverScroll
// event::HoverScroll eHoverScroll;
// eHoverScroll.pos = pos;
// eHoverScroll.scrollDelta = scrollDelta;
// rootWidget->onHoverScroll(eHoverScroll);
// }

// event::Hover
event::Context eHoverContext;
event::Hover eHover;


+ 9
- 1
src/ui/ScrollWidget.cpp View File

@@ -70,10 +70,18 @@ void ScrollWidget::step() {
}

void ScrollWidget::onHover(const event::Hover &e) {
widget::OpaqueWidget::onHover(e);
widget::Widget::onHover(e);
}

void ScrollWidget::onHoverScroll(const event::HoverScroll &e) {
widget::Widget::onHoverScroll(e);
if (e.getConsumed())
return;

// Scroll only if the scrollbars are visible
if (!(horizontalScrollBar->visible || verticalScrollBar->visible))
return;

math::Vec scrollDelta = e.scrollDelta;
// Flip coordinates if shift is held
if ((APP->window->getMods() & WINDOW_MOD_MASK) == GLFW_MOD_SHIFT)


+ 3
- 6
src/ui/Slider.cpp View File

@@ -40,12 +40,9 @@ void Slider::onDragEnd(const event::DragEnd &e) {
APP->window->cursorUnlock();
}

void Slider::onButton(const event::Button &e) {
if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
if (quantity)
quantity->reset();
}
e.consume(this);
void Slider::onDoubleClick(const event::DoubleClick &e) {
if (quantity)
quantity->reset();
}




+ 16
- 0
src/widget/Widget.cpp View File

@@ -16,12 +16,14 @@ Widget::~Widget() {

void Widget::setPos(math::Vec pos) {
box.pos = pos;
// event::Reposition
event::Reposition eReposition;
onReposition(eReposition);
}

void Widget::setSize(math::Vec size) {
box.size = size;
// event::Resize
event::Resize eResize;
onResize(eResize);
}
@@ -30,6 +32,8 @@ math::Rect Widget::getChildrenBoundingBox() {
math::Vec min = math::Vec(INFINITY, INFINITY);
math::Vec max = math::Vec(-INFINITY, -INFINITY);
for (Widget *child : children) {
if (!child->visible)
continue;
min = min.min(child->box.getTopLeft());
max = max.max(child->box.getBottomRight());
}
@@ -64,12 +68,18 @@ void Widget::addChild(Widget *child) {
assert(!child->parent);
child->parent = this;
children.push_back(child);
// event::Add
event::Add eAdd;
child->onAdd(eAdd);
}

void Widget::removeChild(Widget *child) {
assert(child);
// Make sure `this` is the child's parent
assert(child->parent == this);
// event::Remove
event::Remove eRemove;
child->onRemove(eRemove);
// Prepare to remove widget from the event state
APP->event->finalizeWidget(child);
// Delete child from children list
@@ -82,6 +92,9 @@ void Widget::removeChild(Widget *child) {

void Widget::clearChildren() {
for (Widget *child : children) {
// event::Remove
event::Remove eRemove;
child->onRemove(eRemove);
APP->event->finalizeWidget(child);
child->parent = NULL;
delete child;
@@ -94,6 +107,9 @@ void Widget::step() {
Widget *child = *it;
// Delete children if a delete is requested
if (child->requestedDelete) {
// event::Remove
event::Remove eRemove;
child->onRemove(eRemove);
APP->event->finalizeWidget(child);
it = children.erase(it);
child->parent = NULL;


+ 5
- 0
src/window.cpp View File

@@ -136,6 +136,11 @@ static void cursorPosCallback(GLFWwindow *win, double xpos, double ypos) {
window->mousePos = mousePos;

APP->event->handleHover(mousePos, mouseDelta);

// Scroll if middle button is held
if (glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE)) {
APP->event->handleScroll(mousePos, mouseDelta);
}
}

static void cursorEnterCallback(GLFWwindow *win, int entered) {


Loading…
Cancel
Save