BuGUI
BUtton Grid User Interface
Loading...
Searching...
No Matches
grid.hpp
1#pragma once
2#include "init.hpp"
3#include "clear.hpp"
4#include <bugui/io/presser.hpp>
5#include <bugui/concepts/device.hpp>
6#include <bugui/hardware/common.hpp>
7#include <bugui/color/color_converter.hpp>
8#include <bugui/hardware/base_hardware.hpp>
9#include <bugui/hardware/protocol/protocol.hpp>
10
11#include <chrono>
12#include <list>
13
14namespace bugui
15{
18template <typename Device, typename Controller>
19struct grid { };
20
23template <has_grid Device, typename Controller>
24struct grid<Device, Controller>
25 : virtual base_hardware
26 , virtual common<Controller>
27 , virtual init<component<Device, class Device::grid>>
28 , virtual clear<component<Device, class Device::grid>>
29 , virtual protocol<component<Device, class Device::grid>>
30{
31 void set_grid(painter& p) override
32 {
33 this->send([&p, this](auto& formater)
34 {
35 const auto& top_left{p.get_top_left()};
36 const auto& bottom_right{p.get_bottom_right()};
37
38 for (int i{top_left.y}; i < bottom_right.y; i++)
39 for (int j{top_left.x}; j < bottom_right.x; j++)
40 {
41 color c{p.read_clear(j, i)};
42 color_cnvrtr& g{color_grid[i][j]};
43
44 if (g == c) continue;
45
46 g.set(c);
47 formater.set(Device::grid::ids()[i][j], g);
48 }
49 });
50 }
51
52protected:
53 explicit grid()
54 : color_grid{}
55 {
56 this->on_message_received
57 ([this](auto id, auto value)
58 { return set(id, value); });
59 }
60
61 void component_reset()
62 {
63 if constexpr(has_clear<typename Device::grid>)
64 this->clear<class component<Device, class Device::grid>>
65 ::component_reset();
66 else
67 this->send(
68 [this](auto& formater)
69 {
70 for (int i{0}; i < m_height; i++)
71 for (int j{0}; j < m_width; j++)
72 {
73 color c{};
74 color_cnvrtr& g{color_grid[i][j]};
75
76 if (g == c) continue;
77
78 g.set(c);
79 formater.set(Device::grid::ids()[i][j], g);
80 }
81 });
82 }
83
84private:
85 using color_cnvrtr = color_converter
86 <typename color_t<Device, class Device::grid>::type>;
87
88 bool set(uint8_t id, uint8_t midi)
89 { // FIXME : Only set presser if the receiver returns true
90 prsr.set_midi(midi);
91 return on_grid_received(id);
92 }
93
94 bool set(auto id, bool boolean)
95 { // FIXME : Only set presser if the receiver returns true
96 prsr.set_bool(boolean);
97 return on_grid_received(id);
98 }
99
100 bool on_grid_received(auto id)
101 {
102 // FIXME : ignore the next unpress after a double press ?
103 for (int i{0}; i < m_height; i++)
104 for (int j{0}; j < m_width; j++)
105 if (id == Device::grid::ids()[i][j])
106 {
107 prsr.set(j, i);
108 bool down{prsr.get_bool()};
109 auto it{presses.begin()};
110
111 using namespace std;
112 using namespace chrono;
113 auto now{steady_clock::now()};
114
115 while (it != presses.end())
116 {
117 if (!it->dragged && duration_cast<milliseconds>
118 (now - it->t) > 200ms)
119 {
120 it = presses.erase(it);
121 continue;
122 }
123
124 if (it->i == i && it->j == j)
125 { // looking for double press
126 if (down && !it->down)
127 {
128 prsr.button_presser::set_double_press();
129 presses.erase(it);
130 this->controller->handle_presser(prsr);
131 return true;
132 }
133 // looking for drop
134 if (!down && it->down)
135 {
136 if (it->dragged)
137 {
138 prsr.set_drop();
139 presses.erase(it);
140 this->controller->handle_presser(prsr);
141 return true;
142 }
143 }
144 }
145 // looking for drag : presses one cell apart
146 else if (sqrt(pow(it->i - i, 2) +
147 pow(it->j - j, 2)) == 1)
148 {
149 if (!down && it->down)
150 {
151 prsr.set_drag(it->j, it->i);
152 it->dragged = true;
153 // cleanup previous drag
154 it++;
155 while (it != presses.end())
156 {
157 if (it->i == i && it->j == j && it->dragged)
158 {
159 presses.erase(it);
160 break;
161 }
162 else it++;
163 }
164
165 this->controller->handle_presser(prsr);
166 return true;
167 }
168 }
169
170 it++;
171 };
172
173 down ? presses.emplace_front(
174 previous_press{now, i, j, true})
175 : presses.emplace_back(
176 previous_press{now, i , j, false});
177 this->controller->handle_presser(prsr);
178 return true;
179 }
180
181 return false;
182 }
183
184 static constexpr int m_width{Device::grid::ids()[0].size()};
185 static constexpr int m_height{Device::grid::ids().size()};
186 int width() const override { return m_width; }
187 int height() const override { return m_height; }
188
189 color_cnvrtr color_grid[m_width][m_height];
190
191 struct previous_press
192 {
193 std::chrono::time_point<std::chrono::steady_clock> t;
194 int i, j;
195 bool down, dragged{false};
196 };
197
198 std::list<previous_press> presses{};
199 presser prsr;
200};
201
202} // nemaspace bugui
Empty struct for devices or componants that do not provide a command to clear all feedback (LED's,...
Definition clear.hpp:10
void set(const color &color) override
Sets the original color.
Definition color_converter.hpp:62
Generic colour class holding RGBA coloour values. The blinking and pulsing states indicate discrete o...
Definition color.hpp:14
void set_grid(painter &p) override
Pass a reference to the painter object and display it's content to the grid.
Definition grid.hpp:31
Empty struct for devices that do not feature a button grid.
Definition grid.hpp:19
Empty struct for devices that do not provide an initialisation command.
Definition init.hpp:10
Adds 2D information on top of a base_painter. The painter is used in user defined paint funcions to d...
Definition painter.hpp:17
color read_clear(int x, int y)
Retrives the color of the cell at the given coordinates and set it to to black.
Inherits from different protocols, depending on the device's specification.
Definition protocol.hpp:13