add list view and other improvements

This commit is contained in:
hippoz 2022-10-15 00:54:46 +03:00
parent 407a86de79
commit f22979c7cc
No known key found for this signature in database
GPG key ID: 7C52899193467641
9 changed files with 216 additions and 33 deletions

View file

@ -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 = [

View file

@ -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
View 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
View 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 };
};
}

View file

@ -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);

View file

@ -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
};
} }

View file

@ -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;
} }

View file

@ -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();
} }

View file

@ -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;
} }