Skip to content

Commit 8899101

Browse files
refactor(menu_widget): separate scrollable_widget for handling scrolling
1 parent 12eaa6c commit 8899101

File tree

4 files changed

+110
-10
lines changed

4 files changed

+110
-10
lines changed

src/shell/contextmenu/menu_widget.cc

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,8 @@ void mb_shell::menu_widget::update(ui::update_context &ctx) {
303303
bg_submenu->update(forkctx_1);
304304
}
305305

306-
// Update scrollable children (item_widgets) through base class
307306
scrollable_widget::update(ctx);
308307

309-
// Set width for all items
310308
for (auto &item : item_widgets) {
311309
item->width->reset_to(*width);
312310
}
@@ -520,7 +518,6 @@ void mb_shell::menu_widget::render(ui::nanovg_context ctx) {
520518
bg->render(ctx);
521519
}
522520

523-
// Render scrollable content (item_widgets) through base class
524521
scrollable_widget::render(ctx);
525522

526523
auto ctx2 = ctx.with_offset(*x, *y);

src/shell/contextmenu/menu_widget.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <memory>
1212
#include <optional>
1313
#include "shell/widgets/background_widget.h"
14+
#include "scrollable_widget.h"
1415

1516
namespace mb_shell {
1617

@@ -92,21 +93,20 @@ enum class popup_direction {
9293
bottom_right,
9394
};
9495
struct menu_item_widget;
95-
struct menu_widget : public ui::widget_flex {
96-
using super = ui::widget_flex;
96+
struct menu_widget : public scrollable_widget {
97+
using super = scrollable_widget;
9798
float bg_padding_vertical = 6;
9899

99-
float max_height = 99999;
100-
float actual_height = 0;
101-
ui::sp_anim_float scroll_top =
102-
anim_float(0, 200, ui::easing_type::ease_in_out);
103100
std::shared_ptr<background_widget> bg;
104101

105102
std::shared_ptr<background_widget> bg_submenu;
106103
std::shared_ptr<menu_widget> current_submenu;
107104
std::optional<std::weak_ptr<ui::widget>> parent_item_widget;
108105
std::vector<std::shared_ptr<widget>> rendering_submenus;
109-
std::vector<std::shared_ptr<widget>> item_widgets;
106+
107+
// item_widgets is now an alias for scrollable_children from base class
108+
std::vector<std::shared_ptr<widget>>& item_widgets = scrollable_children;
109+
110110
menu_widget *parent_menu = nullptr;
111111

112112
menu menu_data;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include "scrollable_widget.h"
2+
#include "menu_render.h"
3+
#include "nanovg.h"
4+
#include "shell/config.h"
5+
#include <algorithm>
6+
7+
namespace mb_shell {
8+
9+
scrollable_widget::scrollable_widget() : super() {
10+
scroll_top->set_easing(ui::easing_type::ease_in_out);
11+
}
12+
13+
void scrollable_widget::update(ui::update_context &ctx) {
14+
// Handle scroll input when hovered
15+
if (ctx.hovered(this)) {
16+
scroll_top->animate_to(
17+
std::clamp(scroll_top->dest() + ctx.scroll_y * 100,
18+
height->dest() - actual_height, 0.f));
19+
}
20+
widget::update(ctx);
21+
ctx.hovered_hit(this);
22+
23+
// Update scrollable children with scroll offset
24+
update_scrollable_children(ctx);
25+
26+
// Calculate actual height and apply max_height limit
27+
actual_height = height->dest();
28+
height->reset_to(std::min(max_height, height->dest()));
29+
}
30+
31+
void scrollable_widget::render(ui::nanovg_context ctx) {
32+
super::render(ctx);
33+
34+
render_scrollable_children(ctx);
35+
render_scrollbar(ctx);
36+
}
37+
38+
void scrollable_widget::update_scrollable_children(ui::update_context &ctx) {
39+
auto forkctx = ctx.with_offset(*x, *y + *scroll_top);
40+
update_children(forkctx, scrollable_children);
41+
reposition_children_flex(forkctx, scrollable_children);
42+
}
43+
44+
void scrollable_widget::render_scrollable_children(ui::nanovg_context ctx) {
45+
ctx.transaction([&] {
46+
ctx.scissor(*x, *y, *width, *height);
47+
render_children(ctx.with_offset(*x, *y + *scroll_top),
48+
scrollable_children);
49+
});
50+
}
51+
52+
void scrollable_widget::render_scrollbar(ui::nanovg_context ctx) {
53+
// Only render scrollbar if content height exceeds visible height
54+
if (height->dest() < actual_height) {
55+
auto scrollbar_width =
56+
config::current->context_menu.theme.scrollbar_width;
57+
auto scrollbar_height = height->dest() * height->dest() / actual_height;
58+
auto scrollbar_x = width->dest() - scrollbar_width - 2 + *x;
59+
auto scrollbar_y = *y - *scroll_top / (actual_height - height->dest()) *
60+
(height->dest() - scrollbar_height);
61+
62+
float c = menu_render::current.value()->light_color ? 0 : 1;
63+
ctx.fillColor(nvgRGBAf(c, c, c, 0.1));
64+
ctx.fillRoundedRect(
65+
scrollbar_x, scrollbar_y, scrollbar_width, scrollbar_height,
66+
config::current->context_menu.theme.scrollbar_radius);
67+
}
68+
}
69+
70+
} // namespace mb_shell
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#pragma once
2+
#include "breeze_ui/animator.h"
3+
#include "breeze_ui/nanovg_wrapper.h"
4+
#include "breeze_ui/ui.h"
5+
#include "breeze_ui/widget.h"
6+
#include <memory>
7+
#include <vector>
8+
9+
namespace mb_shell {
10+
11+
/**
12+
* A widget that provides scrollable content with vertical scrollbar
13+
* Handles scroll input and renders a scrollbar when content exceeds max_height
14+
*/
15+
struct scrollable_widget : public ui::widget_flex {
16+
using super = ui::widget_flex;
17+
18+
float max_height = 99999;
19+
float actual_height = 0;
20+
ui::sp_anim_float scroll_top =
21+
anim_float(0, 200, ui::easing_type::ease_in_out);
22+
std::vector<std::shared_ptr<ui::widget>> scrollable_children;
23+
24+
scrollable_widget();
25+
26+
void update(ui::update_context &ctx) override;
27+
void render(ui::nanovg_context ctx) override;
28+
void update_scrollable_children(ui::update_context &ctx);
29+
void render_scrollable_children(ui::nanovg_context ctx);
30+
void render_scrollbar(ui::nanovg_context ctx);
31+
};
32+
33+
} // namespace mb_shell

0 commit comments

Comments
 (0)