273 lines
7.8 KiB
C++
273 lines
7.8 KiB
C++
/*
|
|
OmniaFramework - A collection of useful functionality
|
|
Copyright (C) 2025 OmniaX-Dev
|
|
|
|
This file is part of OmniaFramework.
|
|
|
|
OmniaFramework is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
OmniaFramework is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "ListView.hpp"
|
|
#include "../../render/BasicRenderer.hpp"
|
|
#include "../Window.hpp"
|
|
#include "../../../ostd/io/Memory.hpp"
|
|
|
|
namespace ogfx
|
|
{
|
|
namespace gui
|
|
{
|
|
namespace widgets
|
|
{
|
|
void ListView::Item::setText(const String& text)
|
|
{
|
|
if (m_text == text)
|
|
return;
|
|
m_text = text;
|
|
update_dimensions();
|
|
if (m_parent) m_parent->m_extentsDirty = true;
|
|
}
|
|
|
|
void ListView::Item::setFontSize(i32 fontSize)
|
|
{
|
|
if (m_fontSize == fontSize)
|
|
return;
|
|
m_fontSize = fontSize;
|
|
update_dimensions();
|
|
if (m_parent) m_parent->m_extentsDirty = true;
|
|
}
|
|
|
|
void ListView::Item::update_dimensions(void)
|
|
{
|
|
if (!isValid())
|
|
return;
|
|
m_dimensions = m_parent->getWindow().getGFX().getStringDimensions(m_text, m_fontSize);
|
|
m_dimensions += Vec2 { m_padding.x + m_padding.w, m_padding.y + m_padding.h };
|
|
}
|
|
|
|
void ListView::Item::set_selected(stdvec<Item*>& selectionList)
|
|
{
|
|
for (auto& sel : selectionList)
|
|
sel->m_selected = false;
|
|
selectionList.clear();
|
|
selectionList.push_back(this);
|
|
m_selected = true;
|
|
}
|
|
|
|
void ListView::Item::add_selected(stdvec<Item*>& selectionList)
|
|
{
|
|
if (STDVEC_CONTAINS(selectionList, this))
|
|
return;
|
|
selectionList.push_back(this);
|
|
m_selected = true;
|
|
}
|
|
|
|
void ListView::Item::remove_selected(stdvec<Item*>& selectionList)
|
|
{
|
|
if (!STDVEC_CONTAINS(selectionList, this))
|
|
return;
|
|
STDVEC_REMOVE(selectionList, this);
|
|
m_selected = false;
|
|
}
|
|
|
|
|
|
|
|
|
|
ListView& ListView::create(void)
|
|
{
|
|
setPadding({ 0, 0, 0, 0 });
|
|
setTypeName("ogfx::gui::widgets::ListView");
|
|
enableStopEvents();
|
|
enableBackground();
|
|
enableBorder();
|
|
setBackgroundColor({ 160, 160, 160 });
|
|
setBorderColor({ 50, 50, 50 });
|
|
setStylesheetCategoryName("list");
|
|
validate();
|
|
return *this;
|
|
}
|
|
|
|
void ListView::applyTheme(const ostd::Stylesheet& theme)
|
|
{
|
|
setSeparatorLineColor(getThemeValue<Color>(theme, "separatorLineColor", getSeparatorLineColor()));
|
|
enableShowSeparatorLine(getThemeValue<bool>(theme, "showSeparatorLine", isShowSeparatorLineEnabled()));
|
|
setDefaultLineTextColor(getThemeValue<Color>(theme, "item.defaultTextColor", getDefaultLineTextColor()));
|
|
setDefaultLineFontSize(getThemeValue<i32>(theme, "item.defaultFontSize", getDefaultLineFontSize()));
|
|
setDefaultLinePadding(getThemeValue<Rectangle>(theme, "item.defaultPadding", getDefaultLinePadding()));
|
|
setDefaultLineSelectionColor(getThemeValue<Color>(theme, "item.defaultSelectionColor", getDefaultLineSelectionColor()));
|
|
setDefaultLineSelectionTextColor(getThemeValue<Color>(theme, "item.defaultSelectionTextColor", getDefaultLineSelectionTextColor()));
|
|
}
|
|
|
|
void ListView::onDraw(ogfx::BasicRenderer2D& gfx)
|
|
{
|
|
const auto& bounds = getGlobalBounds();
|
|
const auto& content = getContentExtents();
|
|
const f32 scrollY = -getScrollOffset().y;
|
|
const f32 visibleH = getContentBounds().h;
|
|
const f32 visibleStart = scrollY;
|
|
const f32 visibleEnd = scrollY + visibleH;
|
|
Color textColor;
|
|
|
|
f32 y = 0;
|
|
i32 startIdx = 0;
|
|
for (auto& item : m_list)
|
|
{
|
|
if (!item.isValid()) { startIdx++; continue; }
|
|
f32 itemH = item.getDimensions().y;
|
|
if (y + itemH > visibleStart) break;
|
|
y += itemH;
|
|
startIdx++;
|
|
}
|
|
|
|
for (i32 i = startIdx; i < (i32)m_list.size(); i++)
|
|
{
|
|
auto& item = m_list[i];
|
|
if (!item.isValid()) continue;
|
|
f32 itemH = item.getDimensions().y;
|
|
if (y > visibleEnd) break;
|
|
f32 lineW = content.w;
|
|
if (content.w < bounds.w)
|
|
lineW = bounds.w;
|
|
if (item.isSelected())
|
|
{
|
|
textColor = item.getSelectedTextColor();
|
|
gfx.fillRect({ Vec2 { bounds.x, bounds.y + y + item.getPadding().h } + getScrollOffset(), { lineW, itemH } }, item.getSelectedColor());
|
|
}
|
|
else
|
|
textColor = item.getTextColor();
|
|
gfx.drawLine({ Vec2 { bounds.x, bounds.y + y + itemH + item.getPadding().h } + getScrollOffset(), Vec2 { bounds.x + lineW, bounds.y + y + itemH + item.getPadding().h } + getScrollOffset() }, getSeparatorLineColor(), 1);
|
|
gfx.drawString(item, bounds.getPosition() + Vec2 { 0, y } + getScrollOffset() + item.getPadding().getPosition(), textColor, item.getFontSize());
|
|
y += itemH;
|
|
}
|
|
}
|
|
|
|
void ListView::afterDraw(ogfx::BasicRenderer2D& gfx)
|
|
{
|
|
drawScrollbars(gfx);
|
|
}
|
|
|
|
void ListView::onMouseReleased(const Event& event)
|
|
{
|
|
if (event.mouse->button != ogfx::MouseEventData::eButton::Left)
|
|
return;
|
|
if (isMouseInsideAnyScrollbar())
|
|
return;
|
|
|
|
const Vec2 mousePos { event.mouse->position_x, event.mouse->position_y };
|
|
const Vec2 origin = getGlobalBounds().getPosition();
|
|
const Vec2 scroll = getScrollOffset();
|
|
const f32 localY = mousePos.y - origin.y - scroll.y;
|
|
|
|
f32 y = 0;
|
|
for (auto& item : m_list)
|
|
{
|
|
if (!item.isValid()) continue;
|
|
const f32 itemH = item.getDimensions().y;
|
|
if (localY >= y && localY < y + itemH)
|
|
{
|
|
bool wasSelected = item.isSelected();
|
|
item.set_selected(m_selectedList);
|
|
if (!wasSelected && callback_onSelectionChanged)
|
|
callback_onSelectionChanged(m_selectedList);
|
|
event.handle();
|
|
break;
|
|
}
|
|
y += itemH;
|
|
}
|
|
}
|
|
|
|
Rectangle ListView::getContentExtents(void) const
|
|
{
|
|
|
|
if (!m_extentsDirty)
|
|
return m_cachedExtents;
|
|
f32 maxX = 0, maxY = 0;
|
|
for (auto& item : m_list)
|
|
{
|
|
if (!item.isValid()) continue;
|
|
Vec2 size = item.getDimensions();
|
|
maxX = std::max(maxX, size.x);
|
|
maxY += size.y;
|
|
}
|
|
m_cachedExtents = { 0, 0, maxX, maxY };
|
|
m_extentsDirty = false;
|
|
return m_cachedExtents;
|
|
}
|
|
|
|
ListView::Item& ListView::getLine(const String& text)
|
|
{
|
|
for (auto& item : m_list)
|
|
if (item.getText() == text)
|
|
return item;
|
|
return InvalidItem;
|
|
}
|
|
|
|
ListView::Item& ListView::getLine(u32 index)
|
|
{
|
|
if (index < m_list.size())
|
|
return m_list[index];
|
|
return InvalidItem;
|
|
}
|
|
|
|
ListView::Item& ListView::addLine(const String& text)
|
|
{
|
|
Item item { *this };
|
|
item.setTextColor(m_defaultLinetextColor);
|
|
item.setPadding(m_defaultLinepadding);
|
|
item.setFontSize(m_defaultLinefontSize);
|
|
item.setSelectedColor(m_defaultSelectionColor);
|
|
item.setSelectedTextColor(m_defaultSelectionTextColor);
|
|
item.setText(text);
|
|
m_list.push_back(item);
|
|
if (m_list.size() > 0)
|
|
m_list[0].set_selected(m_selectedList);
|
|
m_extentsDirty = true;
|
|
return m_list.back();
|
|
}
|
|
|
|
bool ListView::removeLine(const String& text)
|
|
{
|
|
auto it = std::find_if(m_list.begin(), m_list.end(), [&](const Item& item) {
|
|
return item.getText() == text;
|
|
});
|
|
if (it != m_list.end())
|
|
{
|
|
m_list.erase(it);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ListView::removeLine(u32 index)
|
|
{
|
|
if (!hasLine(index))
|
|
return false;
|
|
m_list.erase(m_list.begin() + index);
|
|
return true;
|
|
}
|
|
|
|
bool ListView::hasLine(const String& text)
|
|
{
|
|
for (auto& item : m_list)
|
|
if (item.getText() == text)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool ListView::hasLine(u32 index)
|
|
{
|
|
return m_list.size() > index;
|
|
}
|
|
}
|
|
}
|
|
}
|