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/BoxLayout.cpp',
|
||||
'./src/Label.cpp',
|
||||
'./src/ListView.cpp',
|
||||
]
|
||||
|
||||
raven_header_files = [
|
||||
|
|
|
@ -20,7 +20,7 @@ void VerticalBoxLayout::run() {
|
|||
}
|
||||
|
||||
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;
|
||||
|
|
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
g_object_unref(layout);
|
||||
|
|
|
@ -27,7 +27,7 @@ RGB accent2 = RGB(0xb16286);
|
|||
|
||||
GenericStyle flat_widget_style {
|
||||
pango_font_description_from_string("sans-serif"),
|
||||
black6,
|
||||
black1,
|
||||
white3,
|
||||
white3,
|
||||
white3,
|
||||
|
@ -38,18 +38,18 @@ GenericStyle flat_widget_style {
|
|||
|
||||
GenericStyle raised_widget_style {
|
||||
pango_font_description_from_string("sans-serif"),
|
||||
black6,
|
||||
black1,
|
||||
white2,
|
||||
white2,
|
||||
white2,
|
||||
0.0,
|
||||
6.0,
|
||||
true,
|
||||
false
|
||||
};
|
||||
|
||||
GenericStyle clear_widget_style {
|
||||
pango_font_description_from_string("sans-serif"),
|
||||
black6,
|
||||
black1,
|
||||
white2,
|
||||
white2,
|
||||
white2,
|
||||
|
@ -60,22 +60,22 @@ GenericStyle clear_widget_style {
|
|||
|
||||
GenericStyle flat_button_style {
|
||||
pango_font_description_from_string("sans-serif"),
|
||||
black6,
|
||||
black1,
|
||||
white3,
|
||||
white2,
|
||||
white1,
|
||||
5.0,
|
||||
6.0,
|
||||
true,
|
||||
true
|
||||
};
|
||||
|
||||
GenericStyle raised_button_style {
|
||||
pango_font_description_from_string("sans-serif"),
|
||||
black6,
|
||||
black1,
|
||||
white2,
|
||||
white1,
|
||||
white0,
|
||||
5.0,
|
||||
6.0,
|
||||
true,
|
||||
true
|
||||
};
|
||||
|
@ -86,14 +86,14 @@ GenericStyle accent_button_style {
|
|||
accent2,
|
||||
accent1,
|
||||
accent0,
|
||||
0.0,
|
||||
6.0,
|
||||
true,
|
||||
true
|
||||
};
|
||||
|
||||
GenericStyle flat_label_style {
|
||||
pango_font_description_from_string("sans-serif"),
|
||||
black6,
|
||||
black1,
|
||||
unused,
|
||||
unused,
|
||||
unused,
|
||||
|
@ -102,4 +102,26 @@ GenericStyle flat_label_style {
|
|||
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 accent_button_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) {
|
||||
on_layout(); // hack
|
||||
|
||||
if (m_layout) {
|
||||
m_layout->run();
|
||||
}
|
||||
|
||||
m_window_relative = compute_window_relative();
|
||||
|
||||
for (auto child : m_children) {
|
||||
child->dispatch_event(event);
|
||||
}
|
||||
|
||||
if (m_layout) {
|
||||
m_layout->run();
|
||||
}
|
||||
|
||||
on_after_layout();
|
||||
}
|
||||
|
||||
|
|
53
src/main.cpp
53
src/main.cpp
|
@ -1,4 +1,6 @@
|
|||
#include "Label.hpp"
|
||||
#include "ColumnLayout.hpp"
|
||||
#include "ListView.hpp"
|
||||
#include "Window.hpp"
|
||||
#include "Widget.hpp"
|
||||
#include "Button.hpp"
|
||||
|
@ -21,31 +23,48 @@ int main() {
|
|||
window.spawn_window();
|
||||
|
||||
auto main_widget = window.set_main_widget<Raven::Widget>();
|
||||
auto main_layout = main_widget->set_layout<Raven::BoxLayout>(Raven::Direction::Vertical);
|
||||
main_layout->slot_pixel(30); // top bar
|
||||
auto main_widget_layout = main_widget->set_layout<Raven::BoxLayout>(Raven::Direction::Horizontal);
|
||||
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>();
|
||||
top_bar->set_style(&Raven::raised_widget_style);
|
||||
top_bar->set_layout<Raven::BoxLayout>(Raven::Direction::Horizontal);
|
||||
auto list_view = main_widget->add<Raven::ListView>();
|
||||
|
||||
auto container_widget = main_widget->add<Raven::Widget>();
|
||||
container_widget->set_style(&Raven::raised_widget_style);
|
||||
auto container_widget_layout = container_widget->set_layout<Raven::BoxLayout>(Raven::Direction::Vertical);
|
||||
container_widget_layout->set_margin(24.0);
|
||||
container_widget_layout->set_spacing(8.0);
|
||||
auto content = main_widget->add<Raven::Widget>();
|
||||
auto content_layout = content->set_layout<Raven::VerticalBoxLayout>();
|
||||
content_layout->set_margin(6.0);
|
||||
content->set_style(&Raven::raised_widget_style);
|
||||
|
||||
auto new_button = top_bar->add<Raven::Button>("add", Raven::Button::Accent);
|
||||
new_button->on_click = [&window, container_widget]() {
|
||||
window.queue_microtask([container_widget]() {
|
||||
container_widget->add<Raven::Button>("hello");
|
||||
auto selected_label = content->add<Raven::Label>("No selection");
|
||||
selected_label->rect().set_max_height(28.0);
|
||||
|
||||
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);
|
||||
remove_button->on_click = [container_widget]() {
|
||||
container_widget->clear_children();
|
||||
next_button->on_click = [list_view]() {
|
||||
list_view->set_active_element(list_view->active_element() + 1);
|
||||
};
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue