add list view and other improvements
This commit is contained in:
parent
407a86de79
commit
f22979c7cc
9 changed files with 216 additions and 33 deletions
|
@ -36,6 +36,7 @@ raven_source_files = [
|
||||||
'./src/ColumnLayout.cpp',
|
'./src/ColumnLayout.cpp',
|
||||||
'./src/BoxLayout.cpp',
|
'./src/BoxLayout.cpp',
|
||||||
'./src/Label.cpp',
|
'./src/Label.cpp',
|
||||||
|
'./src/ListView.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
raven_header_files = [
|
raven_header_files = [
|
||||||
|
|
|
@ -20,7 +20,7 @@ void VerticalBoxLayout::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (child->rect().width() > max_width_so_far) {
|
if (child->rect().width() > max_width_so_far) {
|
||||||
max_width_so_far = child->rect().height();
|
max_width_so_far = child->rect().width();
|
||||||
}
|
}
|
||||||
|
|
||||||
requested_height += child->rect().height() + m_margin;
|
requested_height += child->rect().height() + m_margin;
|
||||||
|
|
93
src/ListView.cpp
Normal file
93
src/ListView.cpp
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#include "ListView.hpp"
|
||||||
|
#include "pango/pango-layout.h"
|
||||||
|
#include "src/Painter.hpp"
|
||||||
|
|
||||||
|
namespace Raven {
|
||||||
|
|
||||||
|
void ListView::on_init() {
|
||||||
|
set_did_init(true);
|
||||||
|
set_style_pure(&flat_listview_style);
|
||||||
|
reflow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListView::elements_updated() {
|
||||||
|
reflow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListView::set_active_element(unsigned int active_element) {
|
||||||
|
if (active_element < 0 || active_element >= elements.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_active_element = active_element;
|
||||||
|
if (on_selection) {
|
||||||
|
on_selection(active_element, elements[active_element]);
|
||||||
|
}
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListView::on_mouse_button(MouseButtonEvent &event) {
|
||||||
|
if (!rect().contains_point(event.point())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.was_left_button_pressed()) {
|
||||||
|
for (unsigned int i = 0; i < elements.size(); i++) {
|
||||||
|
auto element = elements[i];
|
||||||
|
double item_y = m_item_height * i;
|
||||||
|
double event_y = event.point().y() + m_scroll;
|
||||||
|
|
||||||
|
if (event_y > item_y && event_y < item_y + m_item_height) {
|
||||||
|
set_active_element(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event.did_scroll_down()) {
|
||||||
|
m_scroll += m_scroll_step;
|
||||||
|
repaint();
|
||||||
|
} else if (event.did_scroll_up()) {
|
||||||
|
m_scroll -= m_scroll_step;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListView::on_paint() {
|
||||||
|
for (unsigned int i = 0; i < elements.size(); i++) {
|
||||||
|
auto element = elements[i];
|
||||||
|
double y_pos = m_item_height * i;
|
||||||
|
double y = y_pos - m_scroll;
|
||||||
|
|
||||||
|
/* ensure the item is visible before painting */
|
||||||
|
double visible_extents_start = rect().y() + m_scroll;
|
||||||
|
double visible_extents_end = rect().y() + rect().height() + m_scroll;
|
||||||
|
if ((y_pos + m_item_height) < visible_extents_start) {
|
||||||
|
// This item is out of view, however, we cannot exit the loop here as there are candidates for painting ahead.
|
||||||
|
continue;
|
||||||
|
} else if (y_pos > visible_extents_end) {
|
||||||
|
// The item is out of view and we also don't have any further candidates for painting.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* paint: background */
|
||||||
|
{
|
||||||
|
if (m_active_element == i) {
|
||||||
|
painter()->source_rgb(style()->background_active());
|
||||||
|
} else {
|
||||||
|
painter()->source_rgb(style()->background_norm());
|
||||||
|
}
|
||||||
|
painter()->cairo()->rectangle(0, y, item_width(), item_height());
|
||||||
|
painter()->fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* paint: text */
|
||||||
|
{
|
||||||
|
painter()->cairo()->move_to(0, y);
|
||||||
|
painter()->source_rgb(style()->foreground());
|
||||||
|
|
||||||
|
Box item_geometry = {0, 0, item_width(), item_height() };
|
||||||
|
painter()->text(item_geometry, element, PaintTextAlign::Left, PangoEllipsizeMode::PANGO_ELLIPSIZE_END, style()->font_description());
|
||||||
|
painter()->fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
src/ListView.hpp
Normal file
42
src/ListView.hpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/Widget.hpp"
|
||||||
|
|
||||||
|
namespace Raven {
|
||||||
|
|
||||||
|
class ListView : public Raven::Widget {
|
||||||
|
public:
|
||||||
|
ListView()
|
||||||
|
: Raven::Widget() {}
|
||||||
|
|
||||||
|
std::vector<std::string> elements;
|
||||||
|
|
||||||
|
void elements_updated();
|
||||||
|
|
||||||
|
double item_height() { return m_item_height; }
|
||||||
|
void set_item_height(double item_height) { m_item_height = item_height; reflow(); }
|
||||||
|
|
||||||
|
double scroll() { return m_scroll; }
|
||||||
|
void set_scroll(double scroll) { m_scroll = scroll; reflow(); }
|
||||||
|
|
||||||
|
double scroll_step() { return m_scroll_step; }
|
||||||
|
void set_scroll_step(double scroll_step) { m_scroll_step = scroll_step; }
|
||||||
|
|
||||||
|
double item_width() { return rect().width(); }
|
||||||
|
|
||||||
|
unsigned int active_element() { return m_active_element; }
|
||||||
|
void set_active_element(unsigned int active_element);
|
||||||
|
|
||||||
|
std::function<void(unsigned int, std::string)> on_selection { nullptr };
|
||||||
|
protected:
|
||||||
|
void on_paint() override;
|
||||||
|
void on_mouse_button(MouseButtonEvent &event) override;
|
||||||
|
void on_init() override;
|
||||||
|
private:
|
||||||
|
unsigned int m_active_element { 0 };
|
||||||
|
double m_item_height { 26.0 };
|
||||||
|
double m_scroll { 0.0 };
|
||||||
|
double m_scroll_step { 26.0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -72,7 +72,11 @@ void Painter::text(Box &geometry, std::string &text, PaintTextAlign align, Pango
|
||||||
x = ((geometry.width() - font_width) / 2);
|
x = ((geometry.width() - font_width) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cairo->move_to(std::floor(x), std::floor(y));
|
if (m_cairo->has_current_point()) {
|
||||||
|
m_cairo->rel_move_to(x, y);
|
||||||
|
} else {
|
||||||
|
m_cairo->move_to(x, y);
|
||||||
|
}
|
||||||
pango_cairo_show_layout(m_cairo->cobj(), layout);
|
pango_cairo_show_layout(m_cairo->cobj(), layout);
|
||||||
|
|
||||||
g_object_unref(layout);
|
g_object_unref(layout);
|
||||||
|
|
|
@ -27,7 +27,7 @@ RGB accent2 = RGB(0xb16286);
|
||||||
|
|
||||||
GenericStyle flat_widget_style {
|
GenericStyle flat_widget_style {
|
||||||
pango_font_description_from_string("sans-serif"),
|
pango_font_description_from_string("sans-serif"),
|
||||||
black6,
|
black1,
|
||||||
white3,
|
white3,
|
||||||
white3,
|
white3,
|
||||||
white3,
|
white3,
|
||||||
|
@ -38,18 +38,18 @@ GenericStyle flat_widget_style {
|
||||||
|
|
||||||
GenericStyle raised_widget_style {
|
GenericStyle raised_widget_style {
|
||||||
pango_font_description_from_string("sans-serif"),
|
pango_font_description_from_string("sans-serif"),
|
||||||
black6,
|
black1,
|
||||||
white2,
|
white2,
|
||||||
white2,
|
white2,
|
||||||
white2,
|
white2,
|
||||||
0.0,
|
6.0,
|
||||||
true,
|
true,
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
GenericStyle clear_widget_style {
|
GenericStyle clear_widget_style {
|
||||||
pango_font_description_from_string("sans-serif"),
|
pango_font_description_from_string("sans-serif"),
|
||||||
black6,
|
black1,
|
||||||
white2,
|
white2,
|
||||||
white2,
|
white2,
|
||||||
white2,
|
white2,
|
||||||
|
@ -60,22 +60,22 @@ GenericStyle clear_widget_style {
|
||||||
|
|
||||||
GenericStyle flat_button_style {
|
GenericStyle flat_button_style {
|
||||||
pango_font_description_from_string("sans-serif"),
|
pango_font_description_from_string("sans-serif"),
|
||||||
black6,
|
black1,
|
||||||
white3,
|
white3,
|
||||||
white2,
|
white2,
|
||||||
white1,
|
white1,
|
||||||
5.0,
|
6.0,
|
||||||
true,
|
true,
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
GenericStyle raised_button_style {
|
GenericStyle raised_button_style {
|
||||||
pango_font_description_from_string("sans-serif"),
|
pango_font_description_from_string("sans-serif"),
|
||||||
black6,
|
black1,
|
||||||
white2,
|
white2,
|
||||||
white1,
|
white1,
|
||||||
white0,
|
white0,
|
||||||
5.0,
|
6.0,
|
||||||
true,
|
true,
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
@ -86,14 +86,14 @@ GenericStyle accent_button_style {
|
||||||
accent2,
|
accent2,
|
||||||
accent1,
|
accent1,
|
||||||
accent0,
|
accent0,
|
||||||
0.0,
|
6.0,
|
||||||
true,
|
true,
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
GenericStyle flat_label_style {
|
GenericStyle flat_label_style {
|
||||||
pango_font_description_from_string("sans-serif"),
|
pango_font_description_from_string("sans-serif"),
|
||||||
black6,
|
black1,
|
||||||
unused,
|
unused,
|
||||||
unused,
|
unused,
|
||||||
unused,
|
unused,
|
||||||
|
@ -102,4 +102,26 @@ GenericStyle flat_label_style {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
GenericStyle raised_listview_style {
|
||||||
|
pango_font_description_from_string("sans-serif"),
|
||||||
|
black1,
|
||||||
|
white2,
|
||||||
|
white0,
|
||||||
|
accent2,
|
||||||
|
6.0,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
GenericStyle flat_listview_style {
|
||||||
|
pango_font_description_from_string("sans-serif"),
|
||||||
|
black1,
|
||||||
|
white2,
|
||||||
|
white0,
|
||||||
|
accent2,
|
||||||
|
6.0,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,5 +31,7 @@ extern GenericStyle flat_button_style;
|
||||||
extern GenericStyle raised_button_style;
|
extern GenericStyle raised_button_style;
|
||||||
extern GenericStyle accent_button_style;
|
extern GenericStyle accent_button_style;
|
||||||
extern GenericStyle flat_label_style;
|
extern GenericStyle flat_label_style;
|
||||||
|
extern GenericStyle flat_listview_style;
|
||||||
|
extern GenericStyle raised_listview_style;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,16 +194,16 @@ void Widget::handle_repaint_rect(RepaintRectEvent &event) {
|
||||||
void Widget::handle_relayout_subtree(RelayoutSubtreeEvent &event) {
|
void Widget::handle_relayout_subtree(RelayoutSubtreeEvent &event) {
|
||||||
on_layout(); // hack
|
on_layout(); // hack
|
||||||
|
|
||||||
if (m_layout) {
|
|
||||||
m_layout->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_window_relative = compute_window_relative();
|
m_window_relative = compute_window_relative();
|
||||||
|
|
||||||
for (auto child : m_children) {
|
for (auto child : m_children) {
|
||||||
child->dispatch_event(event);
|
child->dispatch_event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_layout) {
|
||||||
|
m_layout->run();
|
||||||
|
}
|
||||||
|
|
||||||
on_after_layout();
|
on_after_layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
53
src/main.cpp
53
src/main.cpp
|
@ -1,4 +1,6 @@
|
||||||
#include "Label.hpp"
|
#include "Label.hpp"
|
||||||
|
#include "ColumnLayout.hpp"
|
||||||
|
#include "ListView.hpp"
|
||||||
#include "Window.hpp"
|
#include "Window.hpp"
|
||||||
#include "Widget.hpp"
|
#include "Widget.hpp"
|
||||||
#include "Button.hpp"
|
#include "Button.hpp"
|
||||||
|
@ -21,31 +23,48 @@ int main() {
|
||||||
window.spawn_window();
|
window.spawn_window();
|
||||||
|
|
||||||
auto main_widget = window.set_main_widget<Raven::Widget>();
|
auto main_widget = window.set_main_widget<Raven::Widget>();
|
||||||
auto main_layout = main_widget->set_layout<Raven::BoxLayout>(Raven::Direction::Vertical);
|
auto main_widget_layout = main_widget->set_layout<Raven::BoxLayout>(Raven::Direction::Horizontal);
|
||||||
main_layout->slot_pixel(30); // top bar
|
main_widget_layout->set_margin(8);
|
||||||
|
main_widget_layout->set_spacing(8);
|
||||||
|
main_widget_layout->slot_pixel(250);
|
||||||
|
main_widget_layout->slot_percent(100.0);
|
||||||
|
|
||||||
auto top_bar = main_widget->add<Raven::Widget>();
|
auto list_view = main_widget->add<Raven::ListView>();
|
||||||
top_bar->set_style(&Raven::raised_widget_style);
|
|
||||||
top_bar->set_layout<Raven::BoxLayout>(Raven::Direction::Horizontal);
|
|
||||||
|
|
||||||
auto container_widget = main_widget->add<Raven::Widget>();
|
auto content = main_widget->add<Raven::Widget>();
|
||||||
container_widget->set_style(&Raven::raised_widget_style);
|
auto content_layout = content->set_layout<Raven::VerticalBoxLayout>();
|
||||||
auto container_widget_layout = container_widget->set_layout<Raven::BoxLayout>(Raven::Direction::Vertical);
|
content_layout->set_margin(6.0);
|
||||||
container_widget_layout->set_margin(24.0);
|
content->set_style(&Raven::raised_widget_style);
|
||||||
container_widget_layout->set_spacing(8.0);
|
|
||||||
|
|
||||||
auto new_button = top_bar->add<Raven::Button>("add", Raven::Button::Accent);
|
auto selected_label = content->add<Raven::Label>("No selection");
|
||||||
new_button->on_click = [&window, container_widget]() {
|
selected_label->rect().set_max_height(28.0);
|
||||||
window.queue_microtask([container_widget]() {
|
|
||||||
container_widget->add<Raven::Button>("hello");
|
auto next_button = content->add<Raven::Button>("Next Item", Raven::Button::Accent);
|
||||||
|
|
||||||
|
auto delete_button = content->add<Raven::Button>("Delete Item");
|
||||||
|
delete_button->set_style(&Raven::raised_button_style);
|
||||||
|
|
||||||
|
delete_button->on_click = [list_view, &window]() {
|
||||||
|
window.queue_microtask([list_view](){
|
||||||
|
list_view->elements.erase(list_view->elements.begin() + list_view->active_element());
|
||||||
|
list_view->set_active_element(list_view->active_element() - 1);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
auto remove_button = top_bar->add<Raven::Button>("remove", Raven::Button::Flat);
|
next_button->on_click = [list_view]() {
|
||||||
remove_button->on_click = [container_widget]() {
|
list_view->set_active_element(list_view->active_element() + 1);
|
||||||
container_widget->clear_children();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
list_view->on_selection = [selected_label](unsigned int index, std::string item) {
|
||||||
|
selected_label->set_text("You have selected: " + item);
|
||||||
|
};
|
||||||
|
|
||||||
|
int i = 1000;
|
||||||
|
while (i --> 0) {
|
||||||
|
list_view->elements.push_back("Item " + std::to_string(i));
|
||||||
|
}
|
||||||
|
list_view->elements_updated();
|
||||||
|
|
||||||
window.run(true);
|
window.run(true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue