ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
ScrollBar.c
Go to the documentation of this file.
1/*
2 * ObjectivelyMVC: Object oriented MVC framework for SDL3 and C.
3 * Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24#include <assert.h>
25
26#include "ScrollBar.h"
27#include "ScrollView.h"
28
29#define _Class _ScrollBar
30
31const EnumName ScrollBarVisibilityNames[] = MakeEnumNames(
32 MakeEnumAlias(ScrollBarShow, show),
33 MakeEnumAlias(ScrollBarHide, hide),
34 MakeEnumAlias(ScrollBarAuto, auto)
35);
36
37#pragma mark - Delegate callbacks
38
42static void didDragHandle(ScrollHandle *handle, float delta) {
43
44 ScrollBar *self = handle->delegate.self;
45
46 if (self->scrollView) {
47
48 const SDL_Rect track = $((View *) self, bounds);
49 const SDL_Size content = $((View *) self->scrollView->contentView, size);
50 const SDL_Rect visible = $((View *) self->scrollView, bounds);
51
52 const int scrollRange = content.h - visible.h;
53 const int travel = track.h - ((View *) self->handle)->frame.h;
54
55 if (scrollRange > 0 && travel > 0) {
56
57 SDL_Point offset = self->scrollView->contentOffset;
58 offset.y -= (int) (delta * ((float) scrollRange / travel));
59
60 $(self->scrollView, scrollToOffset, &offset);
61 ((View *) self)->needsLayout = true;
62 }
63 }
64}
65
66#pragma mark - Object
67
71static void dealloc(Object *self) {
72
73 ScrollBar *this = (ScrollBar *) self;
74
75 release(this->handle);
76
77 super(Object, self, dealloc);
78}
79
80#pragma mark - View
81
85static View *init(View *self) {
86 return (View *) $((ScrollBar *) self, initWithScrollView, NULL);
87}
88
92static void layoutSubviews(View *self) {
93
94 ScrollBar *this = (ScrollBar *) self;
95
96 super(View, self, layoutSubviews);
97
98 if (this->scrollView == NULL || this->scrollView->contentView == NULL) {
99 return;
100 }
101
102 const SDL_Rect track = $(self, bounds);
103 const SDL_Size content = $(this->scrollView->contentView, size);
104 const SDL_Rect visible = $((View *) this->scrollView, bounds);
105
106 View *handle = (View *) this->handle;
107
108 if (content.h <= visible.h || track.h <= 0) {
109 handle->frame = (SDL_Rect) { 0, 0, track.w, track.h };
110 } else {
111
112 const int scrollRange = content.h - visible.h;
113
114 int handleH = (int) ((float) track.h * visible.h / content.h);
115 handleH = clamp(handleH, handle->minSize.h, track.h);
116
117 const int travel = track.h - handleH;
118 const float fraction = clamp((float) (-this->scrollView->contentOffset.y) / scrollRange, 0.f, 1.f);
119
120 handle->frame = (SDL_Rect) { 0, (int) (fraction * travel), track.w, handleH };
121 }
122}
123
127static void respondToEvent(View *self, const SDL_Event *event) {
128
129 ScrollBar *this = (ScrollBar *) self;
130
131 switch (event->type) {
132
133 case SDL_EVENT_MOUSE_BUTTON_DOWN:
134 if (this->scrollView) {
135
136 const SDL_Point point = MakePoint(event->button.x, event->button.y);
137 const SDL_Rect handleFrame = $((View *) this->handle, renderFrame);
138 const SDL_Rect visible = $((View *) this->scrollView, bounds);
139
140 SDL_Point offset = this->scrollView->contentOffset;
141
142 if (point.y < handleFrame.y) {
143 offset.y += visible.h;
144 } else if (point.y > handleFrame.y + handleFrame.h) {
145 offset.y -= visible.h;
146 }
147
148 $(this->scrollView, scrollToOffset, &offset);
149 self->needsLayout = true;
150 }
151 return;
152
153 case SDL_EVENT_MOUSE_BUTTON_UP:
154 $(self, resignTouchResponder);
155 return;
156
157 case SDL_EVENT_MOUSE_MOTION:
158 return;
159
160 default:
161 break;
162 }
163
164 super(View, self, respondToEvent, event);
165}
166
167#pragma mark - ScrollBar
168
174
175 self = (ScrollBar *) super(View, self, initWithFrame, NULL);
176 if (self) {
177
178 $((View *) self, addClassName, "scrollBar");
179 ((View *) self)->clipsSubviews = true;
180
181 self->handle = $(alloc(ScrollHandle), initWithFrame, NULL);
182 assert(self->handle);
183 self->handle->delegate.self = self;
184 self->handle->delegate.didDrag = didDragHandle;
185 $((View *) self, addSubview, (View *) self->handle);
186
187 $(self, setScrollView, scrollView);
188 }
189
190 return self;
191}
192
197static void setScrollView(ScrollBar *self, ScrollView *scrollView) {
198
199 self->scrollView = scrollView;
200
201 ((View *) self)->needsLayout = true;
202}
203
204#pragma mark - Class lifecycle
205
209static void initialize(Class *clazz) {
210
211 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
212
213 ((ViewInterface *) clazz->interface)->init = init;
214 ((ViewInterface *) clazz->interface)->layoutSubviews = layoutSubviews;
215 ((ViewInterface *) clazz->interface)->respondToEvent = respondToEvent;
216
217 ((ScrollBarInterface *) clazz->interface)->initWithScrollView = initWithScrollView;
218 ((ScrollBarInterface *) clazz->interface)->setScrollView = setScrollView;
219}
220
225Class *_ScrollBar(void) {
226 static Class *clazz;
227 static Once once;
228
229 do_once(&once, {
230 clazz = _initialize(&(const ClassDef) {
231 .name = "ScrollBar",
232 .superclass = _View(),
233 .instanceSize = sizeof(ScrollBar),
234 .interfaceOffset = offsetof(ScrollBar, interface),
235 .interfaceSize = sizeof(ScrollBarInterface),
237 });
238 });
239
240 return clazz;
241}
242
243#undef _Class
static Box * initWithFrame(Box *self, const SDL_Rect *frame)
Definition Box.c:92
static SDL_Size size(const Image *self)
Definition Image.c:181
static void addSubview(View *self, View *subview)
Definition PageView.c:49
static void didDragHandle(ScrollHandle *handle, float delta)
ScrollHandleDelegate; maps handle travel onto the ScrollView's content offset.
Definition ScrollBar.c:42
static void setScrollView(ScrollBar *self, ScrollView *scrollView)
Definition ScrollBar.c:197
static View * init(View *self)
Definition ScrollBar.c:85
static void respondToEvent(View *self, const SDL_Event *event)
Definition ScrollBar.c:127
static ScrollBar * initWithScrollView(ScrollBar *self, ScrollView *scrollView)
Definition ScrollBar.c:173
Class * _ScrollBar(void)
Definition ScrollBar.c:225
static void dealloc(Object *self)
Definition ScrollBar.c:71
const EnumName ScrollBarVisibilityNames[]
Definition ScrollBar.c:31
static void initialize(Class *clazz)
Definition ScrollBar.c:209
static void layoutSubviews(View *self)
Definition ScrollBar.c:92
A visible, draggable vertical scrollbar, owned by a ScrollView.
@ ScrollBarHide
Definition ScrollBar.h:68
@ ScrollBarAuto
Definition ScrollBar.h:70
@ ScrollBarShow
Definition ScrollBar.h:69
ScrollViews allow users to pan their internal contents.
static void addClassName(View *self, const char *className)
Definition View.c:159
Class * _View(void)
Definition View.c:2056
static void resignTouchResponder(View *self)
Definition View.c:1500
static SDL_Rect renderFrame(const View *self)
Definition View.c:1444
static SDL_Rect bounds(const View *self)
Definition View.c:492
A draggable scrollbar, owned by a ScrollView.
Definition ScrollBar.h:85
ScrollView * scrollView
The ScrollView this bar drives. Weak reference (not retained).
Definition ScrollBar.h:102
void setScrollView(ScrollBar *self, ScrollView *scrollView)
Binds this ScrollBar to the given ScrollView and syncs the handle.
Definition ScrollBar.c:197
ident self
The delegate self pointer (the ScrollBar).
A draggable scrollbar handle.
ScrollHandleDelegate delegate
The delegate.
ScrollViews allow users to pan their internal contents.
Definition ScrollView.h:61
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition View.h:134
bool needsLayout
If true, this View will layout its subviews before it is drawn.
Definition View.h:222
SDL_Size minSize
The minimum size this View may be resized to during layout.
Definition View.h:212
layoutSubviews(View *self)
Performs layout for this View's immediate subviews.
Definition Box.c:74
SDL_Rect frame
The frame, relative to the superview.
Definition View.h:191