Skip to content

Commit

Permalink
Implement a side panel with block controls
Browse files Browse the repository at this point in the history
Clicking on a block in the flowgraph ones a side panel with controls
to change the block settings. Likewise, clicking on a data signal in a plot
legend opens a similar panel where it's possible to navigate back and forth
in the blocks chain, add new blocks in the chain and change blocks settings.
The panels close after 15 seconds of inactivity.
  • Loading branch information
giucam committed Jul 6, 2023
1 parent 7aece25 commit 900302f
Show file tree
Hide file tree
Showing 14 changed files with 638 additions and 41 deletions.
1 change: 1 addition & 0 deletions src/ui/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class App {
std::array<ImFont *, 2> fontLarge = { nullptr, nullptr }; /// 0: production 1: prototype use
ImFont *fontIcons;
ImFont *fontIconsSolid;
std::chrono::seconds editPaneCloseDelay{ 15 };

private:
App();
Expand Down
14 changes: 7 additions & 7 deletions src/ui/assets/sampleDashboards/DemoDashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ sources:
block: sine source 3
port: 0
color: 4281939916
- name: source for sink 1.out
block: source for sink 1
- name: sink 1
block: sink 1
port: 0
color: 4281816053
- name: source for sink 2.out
block: source for sink 2
- name: sink 2
block: sink 2
port: 0
color: 4284689254
plots:
Expand Down Expand Up @@ -41,7 +41,7 @@ plots:
min: 0.0003612833097577095
max: 0.78648322820663452
sources:
- source for sink 2.out
- sink 2
rect:
- 0
- 8
Expand All @@ -56,10 +56,10 @@ plots:
min: -1.991981029510498
max: 1.9978399276733398
sources:
- source for sink 1.out
- sink 1
rect:
- 8
- 0
- 8
- 8
flowgraphLayout: "{\"nodes\":{\"node:18643992\":{\"location\":{\"x\":278,\"y\":12},\"name\":\"sum sigs\"},\"node:18826368\":{\"location\":{\"x\":502,\"y\":92},\"name\":\"FFT\"},\"node:18827920\":{\"location\":{\"x\":-10,\"y\":91},\"name\":\"sine source 3\"},\"node:18828016\":{\"location\":{\"x\":-10,\"y\":0},\"name\":\"sine source 1\"},\"node:18889008\":{\"location\":{\"x\":-10,\"y\":273},\"name\":\"source for sink 2\"},\"node:18890880\":{\"location\":{\"x\":1056,\"y\":0},\"name\":\"sink 1\"},\"node:18891568\":{\"location\":{\"x\":1056,\"y\":91},\"name\":\"sink 2\"},\"node:6130544\":{\"location\":{\"x\":-10,\"y\":182},\"name\":\"source for sink 1\"}},\"selection\":null,\"view\":{\"scroll\":{\"x\":-26.0907745361328125,\"y\":-25.8306369781494141},\"visible_rect\":{\"max\":{\"x\":1159.2728271484375,\"y\":582.1129150390625},\"min\":{\"x\":-17.3938503265380859,\"y\":-17.2204246520996094}},\"zoom\":1.5}}"
flowgraphLayout: "{\"nodes\":{\"node:18643992\":{\"location\":{\"x\":278,\"y\":12},\"name\":\"sum sigs\"},\"node:18826368\":{\"location\":{\"x\":502,\"y\":92},\"name\":\"FFT\"},\"node:18827920\":{\"location\":{\"x\":-10,\"y\":91},\"name\":\"sine source 3\"},\"node:18828016\":{\"location\":{\"x\":-10,\"y\":0},\"name\":\"sine source 1\"},\"node:18889008\":{\"location\":{\"x\":-10,\"y\":273},\"name\":\"source for sink 2\"},\"node:18890880\":{\"location\":{\"x\":1056,\"y\":0},\"name\":\"sink 1\"},\"node:18891568\":{\"location\":{\"x\":1056,\"y\":91},\"name\":\"sink 2\"},\"node:6130544\":{\"location\":{\"x\":-10,\"y\":182},\"name\":\"source for sink 1\"}},\"selection\":null,\"view\":{\"scroll\":{\"x\":-26.0907745361328125,\"y\":-25.8306369781494141},\"visible_rect\":{\"max\":{\"x\":1159.2728271484375,\"y\":582.1129150390625},\"min\":{\"x\":-17.3938503265380859,\"y\":-17.2204246520996094}},\"zoom\":1.5}}"
12 changes: 6 additions & 6 deletions src/ui/assets/sampleDashboards/ExtendedDemoDashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ sources:
block: sine source 6
port: 0
color: 4285336815
- name: source for sink 1.out
block: source for sink 1
- name: sink 1
block: sink 1
port: 0
color: 4285977777
- name: source for sink 2.out
block: source for sink 2
- name: sink 2
block: sink 2
port: 0
color: 4286162635
plots:
Expand Down Expand Up @@ -48,7 +48,7 @@ plots:
min: -3.2323789596557617
max: 3.2323942184448242
sources:
- source for sink 1.out
- sink 1
rect:
- 8
- 0
Expand Down Expand Up @@ -78,7 +78,7 @@ plots:
min: 0
max: 1
sources:
- source for sink 2.out
- sink 2
rect:
- 8
- 8
Expand Down
22 changes: 20 additions & 2 deletions src/ui/dashboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,17 @@ Dashboard::Dashboard(const std::shared_ptr<DashboardDescription> &desc)
m_desc->lastUsed = std::chrono::floor<std::chrono::days>(std::chrono::system_clock::now());

localFlowGraph.sourceBlockAddedCallback = [this](Block *b) {
if (dynamic_cast<DataSinkSource *>(b)) {
return;
}
for (int i = 0; i < b->type->outputs.size(); ++i) {
auto name = fmt::format("{}.{}", b->name, b->type->outputs[i].name);
m_sources.insert({ b, i, name, randomColor() });
}
};
localFlowGraph.sinkBlockAddedCallback = [this](Block *b) {
m_sources.insert({ b, -1, b->name, randomColor() });
};
localFlowGraph.blockDeletedCallback = [this](Block *b) {
for (auto &p : m_plots) {
p.sources.erase(std::remove_if(p.sources.begin(), p.sources.end(), [=](auto *s) { return s->block == b; }),
Expand All @@ -240,6 +246,17 @@ Dashboard::Dashboard(const std::shared_ptr<DashboardDescription> &desc)
Dashboard::~Dashboard() {
}

DataSink *Dashboard::createSink() {
int n = localFlowGraph.sinkBlocks().size() + 1;
auto name = fmt::format("sink {}", n);
auto sink = std::make_unique<DigitizerUi::DataSink>(name);
auto sinkptr = sink.get();
localFlowGraph.addSinkBlock(std::move(sink));
name = fmt::format("source for sink {}", n);
localFlowGraph.addSourceBlock(std::make_unique<DigitizerUi::DataSinkSource>(name));
return sinkptr;
}

void Dashboard::setNewDescription(const std::shared_ptr<DashboardDescription> &desc) {
m_desc = desc;
}
Expand Down Expand Up @@ -299,11 +316,11 @@ void Dashboard::doLoad(const std::string &desc) {
auto colorNum = color.as<uint32_t>();

auto source = std::find_if(m_sources.begin(), m_sources.end(), [&](const auto &s) {
return s.block->name == blockStr && s.port == portNum;
return s.name == nameStr;
});
if (source == m_sources.end()) {
fmt::print("Unable to find the source '{}.{}'\n", blockStr, portNum);
return;
continue;
}

source->name = nameStr;
Expand Down Expand Up @@ -360,6 +377,7 @@ void Dashboard::doLoad(const std::string &desc) {
auto source = std::find_if(m_sources.begin(), m_sources.end(), [&](const auto &s) { return s.name == str; });
if (source == m_sources.end()) {
fmt::print("Unable to find source {}\n", str);
continue;
}
plot.sources.push_back(&*source);
}
Expand Down
3 changes: 3 additions & 0 deletions src/ui/dashboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ namespace DigitizerUi {
class Block;
class FlowGraph;
struct DashboardDescription;
class DataSink;

struct DashboardSource {
~DashboardSource() noexcept;
Expand Down Expand Up @@ -117,6 +118,8 @@ class Dashboard {

inline auto &remoteServices() { return m_services; }

DataSink *createSink();

FlowGraph localFlowGraph;

private:
Expand Down
57 changes: 51 additions & 6 deletions src/ui/dashboardpage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,19 +286,26 @@ void DashboardPage::drawPlot(DigitizerUi::Dashboard::Plot &plot) noexcept {
auto color = ImGui::ColorConvertU32ToFloat4(source->color);
ImPlot::SetNextLineStyle(color);

const auto &port = const_cast<const Block *>(source->block)->outputs()[source->port];
const auto [dataType, dataSet] = [&]() -> std::tuple<DataType, DataSet> {
if (auto *sink = dynamic_cast<DataSink *>(source->block)) {
return { sink->dataType, sink->data };
}
auto &port = const_cast<const Block *>(source->block)->outputs()[source->port];
return { port.type, port.dataSet };
}();

if (port.dataSet.empty()) {
ImPlot::HideNextItem(false, ImPlotCond_Always);
if (dataSet.empty()) {
// Plot one single dummy value so that the sink shows up in the plot legend
float v = 0;
if (source->visible) {
ImPlot::PlotLine(source->name.c_str(), &v, 1);
}
} else {
switch (port.type) {
switch (dataType) {
case DigitizerUi::DataType::Float32: {
if (source->visible) {
auto values = port.dataSet.asFloat32();
auto values = dataSet.asFloat32();
ImPlot::PlotLine(source->name.c_str(), values.data(), values.size());
}
break;
Expand All @@ -320,6 +327,26 @@ void DashboardPage::drawPlot(DigitizerUi::Dashboard::Plot &plot) noexcept {
}

void DashboardPage::draw(App *app, Dashboard *dashboard, Mode mode) noexcept {
const float left = ImGui::GetCursorPosX();
const float top = ImGui::GetCursorPosY();
const ImVec2 size = ImGui::GetContentRegionAvail();

const bool horizontalSplit = size.x > size.y;
constexpr float splitterWidth = 6;
constexpr float halfSplitterWidth = splitterWidth / 2.f;
const float ratio = m_editPane.block ? ImGuiUtils::splitter(size, horizontalSplit, splitterWidth, 0.2f) : 0.f;

ImGui::SetCursorPosX(left);
ImGui::SetCursorPosY(top);

ImGui::BeginChild("##plots", horizontalSplit ? ImVec2(size.x * (1.f - ratio) - halfSplitterWidth, size.y) :
ImVec2(size.x, size.y * (1.f - ratio) - halfSplitterWidth),
false, ImGuiWindowFlags_NoScrollbar);

if (ImGui::IsWindowHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
m_editPane.block = nullptr;
}

// Plots
ImGui::BeginGroup();
drawPlots(app, mode, dashboard);
Expand Down Expand Up @@ -369,6 +396,16 @@ void DashboardPage::draw(App *app, Dashboard *dashboard, Mode mode) noexcept {
}
ImGui::EndGroup(); // Legend
legend_box.y = std::floor(ImGui::GetItemRectSize().y * 1.5f);

ImGui::EndChild();

if (horizontalSplit) {
const float w = size.x * ratio;
ImGuiUtils::drawBlockControlsPanel(m_editPane, { left + size.x - w + halfSplitterWidth, top }, { w - halfSplitterWidth, size.y }, true);
} else {
const float h = size.y * ratio;
ImGuiUtils::drawBlockControlsPanel(m_editPane, { left, top + size.y - h + halfSplitterWidth }, { size.x, h - halfSplitterWidth}, false);
}
}

void DashboardPage::drawPlots(App *app, DigitizerUi::DashboardPage::Mode mode, Dashboard *dashboard) {
Expand Down Expand Up @@ -469,6 +506,12 @@ void DashboardPage::drawPlots(App *app, DigitizerUi::DashboardPage::Mode mode, D
for (const auto &s : plot.sources) {
if (ImPlot::IsLegendEntryHovered(s->name.c_str())) {
plotItemHovered = true;

if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
m_editPane.block = s->block;
m_editPane.closeTime = std::chrono::system_clock::now() + App::instance().editPaneCloseDelay;
}

break;
}
}
Expand Down Expand Up @@ -521,7 +564,7 @@ void DashboardPage::drawLegend(App *app, Dashboard *dashboard, const DashboardPa
ImGui::BeginGroup();

const auto legend_item = [](const ImVec4 &color, std::string_view text, bool enabled = true) -> bool {
const ImVec2 cursorPos = ImGui::GetCursorPos();
const ImVec2 cursorPos = ImGui::GetCursorScreenPos();

// Draw colored rectangle
const ImVec4 modifiedColor = enabled ? color : ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
Expand All @@ -545,7 +588,9 @@ void DashboardPage::drawLegend(App *app, Dashboard *dashboard, const DashboardPa
Dashboard::Source &signal = *iter;
auto color = ImGui::ColorConvertU32ToFloat4(signal.color);
if (legend_item(color, signal.name, signal.visible)) {
signal.visible = !signal.visible;
fmt::print("click\n");
m_editPane.block = signal.block;
m_editPane.closeTime = std::chrono::system_clock::now() + App::instance().editPaneCloseDelay;
}
legend_box.x += ImGui::GetItemRectSize().x;

Expand Down
10 changes: 7 additions & 3 deletions src/ui/dashboardpage.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

#include "grid_layout.h"
#include <imgui.h>
#include <stack>
#include <string>
#include <vector>
#include "imguiutils.h"

namespace DigitizerUi {

Expand All @@ -26,7 +28,7 @@ class DashboardPage {
};

private:
ImVec2 pane_size{ 0, 0 }; // updated by draw(...)
ImVec2 pane_size{ 0, 0 }; // updated by drawPlots(...)
ImVec2 legend_box{ 500, 40 }; // updated by drawLegend(...)
GridLayout plot_layout;

Expand All @@ -48,15 +50,17 @@ class DashboardPage {

public:
void draw(App *app, Dashboard *Dashboard, Mode mode = Mode::View) noexcept;
void newPlot(Dashboard *dashboard);

private:
void drawPlots(App *app, DigitizerUi::DashboardPage::Mode mode, Dashboard *dashboard);
void drawGrid(float w, float h);
void drawLegend(App *app, Dashboard *dashboard, const Mode &mode) noexcept;
void newPlot(Dashboard *dashboard);
static void drawPlot(DigitizerUi::Dashboard::Plot &plot) noexcept;

ImGuiUtils::BlockControlsPanel m_editPane;
};

} // namespace DigitizerUi

#endif // DASHBOARDPAGE_H
#endif // DASHBOARDPAGE_H
6 changes: 5 additions & 1 deletion src/ui/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,9 @@ void FlowGraph::addSourceBlock(std::unique_ptr<Block> &&block) {
void FlowGraph::addSinkBlock(std::unique_ptr<Block> &&block) {
block->m_flowGraph = this;
block->update();
if (sinkBlockAddedCallback) {
sinkBlockAddedCallback(block.get());
}
m_sinkBlocks.push_back(std::move(block));
}

Expand Down Expand Up @@ -632,7 +635,7 @@ void FlowGraph::deleteBlock(Block *block) {
}
}

void FlowGraph::connect(Block::Port *a, Block::Port *b) {
Connection *FlowGraph::connect(Block::Port *a, Block::Port *b) {
assert(a->kind != b->kind);
// make sure a is the output and b the input
if (a->kind == Block::Port::Kind::Input) {
Expand All @@ -641,6 +644,7 @@ void FlowGraph::connect(Block::Port *a, Block::Port *b) {
auto it = m_connections.insert(Connection(a, b));
a->connections.push_back(&(*it));
b->connections.push_back(&(*it));
return &(*it);
}

void FlowGraph::disconnect(Connection *c) {
Expand Down
6 changes: 4 additions & 2 deletions src/ui/flowgraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ class Block {
const std::string name;
const std::string id;

protected:
// protected:
auto &inputs() { return m_inputs; }
auto &outputs() { return m_outputs; }

private:
Expand Down Expand Up @@ -246,7 +247,7 @@ class FlowGraph {
void addSourceBlock(std::unique_ptr<Block> &&block);
void addSinkBlock(std::unique_ptr<Block> &&block);

void connect(Block::Port *a, Block::Port *b);
Connection *connect(Block::Port *a, Block::Port *b);

void disconnect(Connection *c);

Expand All @@ -257,6 +258,7 @@ class FlowGraph {
void registerRemoteSource(std::unique_ptr<BlockType> &&type, std::string_view uri);

std::function<void(Block *)> sourceBlockAddedCallback;
std::function<void(Block *)> sinkBlockAddedCallback;
std::function<void(Block *)> blockDeletedCallback;

private:
Expand Down
Loading

0 comments on commit 900302f

Please sign in to comment.