Stand/Stand/Grid.cpp
2024-10-16 11:20:42 +08:00

293 lines
5.9 KiB
C++

#include "Grid.hpp"
#include <algorithm>
#include "GridItem.hpp"
#include "Gui.hpp"
#include "MenuGrid.hpp"
#include "Renderer.hpp"
#include "Worker.hpp"
#define spacer_x spacer_size
#define spacer_y spacer_size
namespace Stand
{
Grid::Grid(Position2d origin, int16_t spacer_size)
: origin(std::move(origin)), spacer_size(spacer_size), items(soup::make_shared<std::vector<std::unique_ptr<GridItem>>>())
{
}
Grid::Grid(int16_t origin_x, int16_t origin_y, int16_t spacer_size)
: Grid(Position2d{ origin_x, origin_y }, spacer_size)
{
}
void Grid::updateSetting(const std::function<void()>& impl)
{
if (g_gui.canUpdateThemeSetting())
{
impl();
update();
}
}
void Grid::updateSettingSimple(const std::function<void()>& impl)
{
if (g_gui.canUpdateThemeSetting())
{
impl();
updatePositions();
}
}
void Grid::update()
{
if (!content_update_queued)
{
content_update_queued = true;
Worker::queueJob([this]
{
content_update_queued = false;
updateNow();
});
}
}
void Grid::updateNow()
{
auto items_draft = soup::make_shared<std::vector<std::unique_ptr<GridItem>>>();
populate(*items_draft);
std::sort(items_draft->begin(), items_draft->end(), [](const std::unique_ptr<GridItem>& a, const std::unique_ptr<GridItem>& b)
{
return a->priority < b->priority;
});
setPositions(*items_draft);
items = std::move(items_draft);
predraw_flags = 0;
}
void Grid::updatePositions()
{
predraw_flags |= GRID_UPDATE_POSITIONS;
}
void Grid::setPositions(const std::vector<std::unique_ptr<GridItem>>& items, size_t recursions) const
{
GridItem* last_item = nullptr;
{
auto x = origin.x;
auto y = origin.y;
for (auto& item : items)
{
if (item->keep_pos)
{
item->keep_pos = false;
x = item->x;
y = item->y;
}
else
{
if (item->force_alignment_to != nullptr)
{
last_item = item->force_alignment_to;
x = last_item->x;
y = last_item->y;
}
if (last_item == nullptr)
{
switch (item->alignment_relative_to_last)
{
case ALIGN_TOP_CENTRE:
y -= item->height;
break;
}
}
else
{
switch (item->alignment_relative_to_last)
{
case ALIGN_BOTTOM_LEFT:
y += (last_item->height + spacer_y);
break;
case ALIGN_TOP_RIGHT:
x += (last_item->width + spacer_x);
break;
case ALIGN_TOP_LEFT:
x -= (item->width + spacer_x);
if (auto* conflict_item = getOccupantNarrow(items, x + item->width - 1, y + 1, item.get()))
{
y += (conflict_item->height + spacer_y);
}
break;
case ALIGN_BOTTOM_RIGHT:
x -= (item->width - last_item->width);
break;
case ALIGN_TOP_CENTRE:
y -= (item->height + spacer_y);
break;
}
}
item->x = x;
item->y = y;
item->onPositioned();
}
last_item = item.get();
}
}
last_item = nullptr;
bool need_redo = false;
for (auto& item : items)
{
if (item->force_alignment_to != nullptr)
{
last_item = item->force_alignment_to;
}
switch (item->alignment_relative_to_last)
{
case ALIGN_BOTTOM_LEFT:
if (auto* conflict_item = getOccupant(items, item->x + item->width - 1, item->y + 1, item.get()))
{
item->x -= ((item->x + item->width) - conflict_item->x) + spacer_x;
item->keep_pos = true;
need_redo = true;
}
break;
}
last_item = item.get();
}
if (need_redo && ++recursions <= items.size())
{
setPositions(items, recursions);
}
}
GridItem* Grid::getOccupant(const std::vector<std::unique_ptr<GridItem>>& items, int16_t x, int16_t y)
{
for (const auto& item : items)
{
if (item->occupies(x, y))
{
return item.get();
}
}
return nullptr;
}
GridItem* Grid::getOccupant(const std::vector<std::unique_ptr<GridItem>>& items, int16_t x, int16_t y, GridItem* exclude)
{
for (const auto& item : items)
{
if (item.get() != exclude && item->occupies(x, y))
{
return item.get();
}
}
return nullptr;
}
GridItem* Grid::getOccupantNarrow(const std::vector<std::unique_ptr<GridItem>>& items, int16_t x, int16_t y, GridItem* stop_at)
{
for (const auto& item : items)
{
if (item.get() == stop_at)
{
break;
}
if (item->occupies(x, y))
{
return item.get();
}
}
return nullptr;
}
GridItem* Grid::getItemByType(GridItemType target) const
{
auto items_capture{ items };
return getItemByType(*items_capture, target);
}
GridItem* Grid::getItemByType(const std::vector<std::unique_ptr<GridItem>>& items, GridItemType target)
{
for (const auto& item : items)
{
if (item->type == target)
{
return item.get();
}
}
return nullptr;
}
void Grid::draw()
{
auto items_capture{ items };
if (predraw_flags != 0)
{
if (predraw_flags & GRID_UPDATE_POSITIONS)
{
setPositions(*items_capture);
}
if (predraw_flags & GRID_UPDATE_MENU_ADDRESSBAR)
{
((MenuGrid*)this)->doUpdateAddress(*items_capture);
}
predraw_flags = 0;
}
if (border_width != 0)
{
for (const auto& item : *items_capture)
{
g_renderer.drawBorderH(item->x, item->y, item->width, item->height, border_width, border_colour, border_rounded);
}
}
for (const auto& item : *items_capture)
{
item->draw();
}
}
void Grid::getBounds(int16_t& x1, int16_t& y1, int16_t& x2, int16_t& y2) const
{
x1 = SHRT_MAX;
y1 = SHRT_MAX;
x2 = 0;
y2 = 0;
auto items_capture{ items };
for (const auto& item : *items_capture)
{
if (item->x < x1)
{
x1 = item->x;
}
if (item->y < y1)
{
y1 = item->y;
}
auto item_x2 = item->x + item->width;
auto item_y2 = item->y + item->height;
if (item_x2 > x2)
{
x2 = item_x2;
}
if (item_y2 > y2)
{
y2 = item_y2;
}
}
}
void Grid::getDimensions(int16_t& x, int16_t& y, int16_t& width, int16_t& height) const
{
getBounds(x, y, width, height);
width -= x;
height -= y;
}
}