ObjectivelyMVC 0.1.0
Object oriented MVC framework for OpenGL, SDL2 and GNU C
ScrollView.c
Go to the documentation of this file.
1/*
2 * ObjectivelyMVC: Object oriented MVC framework for OpenGL, SDL2 and GNU 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 "ScrollView.h"
27
28#define _Class _ScrollView
29
30#pragma mark - Object
31
35static void dealloc(Object *self) {
36
37 ScrollView *this = (ScrollView *) self;
38
39 release(this->contentView);
40
41 super(Object, self, dealloc);
42}
43
44#pragma mark - View
45
49static void layoutSubviews(View *self) {
50
51 super(View, self, layoutSubviews);
52
53 ScrollView *this = (ScrollView *) self;
54
55 if (this->contentView) {
56 this->contentView->frame.x = this->contentOffset.x;
57 this->contentView->frame.y = this->contentOffset.y;
58
59 $(this->contentView, sizeToContain);
60 }
61}
62
63#pragma mark - Control
64
68static _Bool captureEvent(Control *self, const SDL_Event *event) {
69
70 ScrollView *this = (ScrollView *) self;
71
72 if (event->type == SDL_MOUSEMOTION && (event->motion.state & SDL_BUTTON_LMASK)) {
74
75 SDL_Point offset = this->contentOffset;
76
77 offset.x += event->motion.xrel;
78 offset.y += event->motion.yrel;
79
80 $(this, scrollToOffset, &offset);
81 return true;
82 } else if (event->type == SDL_MOUSEWHEEL) {
83 SDL_Point offset = this->contentOffset;
84
85 offset.x -= event->wheel.x * this->step;
86 offset.y += event->wheel.y * this->step;
87
88 $(this, scrollToOffset, &offset);
89 return true;
90 } else if (event->type == SDL_MOUSEBUTTONUP && (event->button.button & SDL_BUTTON_LMASK)) {
91
92 if ($(self, isHighlighted)) {
93 self->state &= ~ControlStateHighlighted;
94 return true;
95 }
96 }
97
98 return super(Control, self, captureEvent, event);
99}
100
101#pragma mark - ScrollView
102
107static ScrollView *initWithFrame(ScrollView *self, const SDL_Rect *frame) {
108
109 self = (ScrollView *) super(Control, self, initWithFrame, frame);
110 if (self) {
112 }
113
114 return self;
115}
116
121static void scrollToOffset(ScrollView *self, const SDL_Point *offset) {
122
123 if (self->contentView) {
124 const SDL_Size contentSize = $(self->contentView, size);
125 const SDL_Rect bounds = $((View *) self, bounds);
126
127 if (contentSize.w > bounds.w) {
128 self->contentOffset.x = clamp(offset->x, -(contentSize.w - bounds.w), 0);
129 } else {
130 self->contentOffset.x = 0;
131 }
132
133 if (contentSize.h > bounds.h) {
134 self->contentOffset.y = clamp(offset->y, -(contentSize.h - bounds.h), 0);
135 } else {
136 self->contentOffset.y = 0;
137 }
138
139 } else {
140 self->contentOffset.x = self->contentOffset.y = 0;
141 }
142
143 self->control.view.needsLayout = true;
144}
145
150static void setContentView(ScrollView *self, View *contentView) {
151
152 if (contentView != self->contentView) {
153
154 if (self->contentView) {
155 $(self->contentView, removeClassName, "contentView");
156 $((View *) self, removeSubview, self->contentView);
157 self->contentView = release(self->contentView);
158 }
159
160 if (contentView) {
161 $(contentView, addClassName, "contentView");
162 $((View *) self, addSubview, contentView);
163 self->contentView = retain(contentView);
164 }
165
166 $(self, scrollToOffset, &MakePoint(0, 0));
167 }
168}
169
170#pragma mark - Class lifecycle
171
175static void initialize(Class *clazz) {
176
177 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
178
179 ((ViewInterface *) clazz->interface)->layoutSubviews = layoutSubviews;
180
181 ((ControlInterface *) clazz->interface)->captureEvent = captureEvent;
182
183 ((ScrollViewInterface *) clazz->interface)->initWithFrame = initWithFrame;
184 ((ScrollViewInterface *) clazz->interface)->scrollToOffset = scrollToOffset;
185 ((ScrollViewInterface *) clazz->interface)->setContentView = setContentView;
186}
187
192Class *_ScrollView(void) {
193 static Class *clazz;
194 static Once once;
195
196 do_once(&once, {
197 clazz = _initialize(&(const ClassDef) {
198 .name = "ScrollView",
199 .superclass = _Control(),
200 .instanceSize = sizeof(ScrollView),
201 .interfaceOffset = offsetof(ScrollView, interface),
202 .interfaceSize = sizeof(ScrollViewInterface),
204 });
205 });
206
207 return clazz;
208}
209
210#undef _Class
@ ControlStateHighlighted
Definition: Control.h:68
static void dealloc(Object *self)
Definition: ScrollView.c:35
static void initialize(Class *clazz)
Definition: ScrollView.c:175
ScrollViews allow users to pan their internal contents.
#define DEFAULT_SCROLL_VIEW_STEP
Definition: ScrollView.h:33
#define MakePoint(x, y)
Creates an SDL_Point with the given coordinates.
Definition: Types.h:69
Box * initWithFrame(Box *self, const SDL_Rect *frame)
Initializes this Box with the given frame.
Definition: Box.c:92
Controls are Views which capture events and dispatch Actions.
Definition: Control.h:83
_Bool captureEvent(Control *self, const SDL_Event *event)
Captures a given event, potentially altering the state of this Control.
Definition: Button.c:76
Class * _Control(void)
The Control archetype.
Definition: Control.c:379
unsigned int state
The bit mask of ControlState.
Definition: Control.h:110
View view
The superclass.
Definition: Control.h:88
_Bool isHighlighted(const Control *self)
Definition: Control.c:317
SDL_Size size(const Image *self)
Definition: Image.c:181
SDL_Size contentSize(const Panel *self)
Definition: Panel.c:166
The SDL_Size type.
Definition: Types.h:62
int w
Definition: Types.h:63
int h
Definition: Types.h:63
ScrollViews allow users to pan their internal contents.
Definition: ScrollView.h:62
static void scrollToOffset(ScrollView *self, const SDL_Point *offset)
Scrolls the content View to the specified offset.
Definition: ScrollView.c:121
void setContentView(ScrollView *self, View *contentView)
Sets the content view of this ScrollView.
Definition: ScrollView.c:150
Class * _ScrollView(void)
The ScrollView archetype.
Definition: ScrollView.c:192
float step
The scroll step, in pixels.
Definition: ScrollView.h:88
View * contentView
The content View.
Definition: ScrollView.h:83
Control control
The superclass.
Definition: ScrollView.h:67
SDL_Point contentOffset
The content View offset.
Definition: ScrollView.h:78
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition: View.h:133
SDL_Rect bounds(const View *self)
Definition: View.c:394
_Bool needsLayout
If true, this View will layout its subviews before it is drawn.
Definition: View.h:221
void addSubview(View *self, View *subview)
Adds a subview to this view, to be drawn above its siblings.
Definition: PageView.c:35
void addClassName(View *self, const char *className)
Adds the given class name to this View.
Definition: View.c:120
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:190
void sizeToContain(View *self)
Resizes this View to contain its subviews.
Definition: View.c:1485
void removeClassName(View *self, const char *className)
Removes the given class name to this View.
Definition: View.c:1158
void removeSubview(View *self, View *subview)
Removes the given subview from this View.
Definition: PageView.c:58