31#include <Objectively/Number.h>
32#include <Objectively/String.h>
46 double nearest = DBL_MAX;
48 for (
size_t i = 0; i < self->
values->count; i++) {
49 const double delta = fabs(VectorValue(self->
values,
double, i) - value);
50 if (delta < nearest) {
65 if (self->
values->count < 2) {
71 return clamp((self->
value - self->
min) / (self->
max - self->
min), 0.0, 1.0);
79 fraction = clamp(fraction, 0.0, 1.0);
82 return VectorValue(self->
values,
double, (
size_t) round(fraction * (self->
values->count - 1)));
85 const double value = self->
min + (self->
max - self->
min) * fraction;
88 return clamp(round(value / self->
step) * self->
step, self->
min, self->
max);
101 (ssize_t) 0, (ssize_t) self->
values->count - 1);
102 return VectorValue(self->
values,
double, index);
105 const double step = self->
step ?: (self->
max - self->
min) / 20.0;
106 return self->
value + step * steps;
118 memset(&this->delegate, 0,
sizeof(this->delegate));
121 release(this->handle);
122 release(this->label);
123 release(this->values);
124 release(this->labels);
126 free(this->labelFormat);
138 const Array *array = cast(Array, obj);
139 assert(array->count);
141 Vector *values = $(alloc(Vector), initWithSize,
sizeof(
double));
144 for (
size_t i = 0; i < array->count; i++) {
145 double value = cast(Number, $(array, objectAtIndex, i))->value;
146 $(values, add, &value);
149 if (values->count < 2) {
150 MVC_LogWarn(
"Inlet %s wanted 2 or more values but received %zd\n", inlet->
name, values->count);
153 release(*(Vector **) inlet->
dest);
154 *(Vector **) inlet->
dest = values;
162 const Array *array = cast(Array, obj);
163 for (
size_t i = 0; i < array->count; i++) {
164 cast(String, $(array, objectAtIndex, i));
167 Array *labels = cast(Array, $((Object *) array, copy));
168 release(*(Array **) inlet->
dest);
169 *(Array **) inlet->
dest = labels;
181 double value = this->value;
197 $(self, bind, inlets, dictionary);
199 if (this->labels && (!this->values || this->labels->count != this->values->count)) {
200 MVC_LogWarn(
"Slider labels must have the same count as values\n");
201 this->labels = release(this->labels);
229 if (this->values || this->max > this->min) {
240 for (
size_t i = 0; i < this->values->count; i++) {
243 const char *characters;
245 characters = cast(String, $(this->labels, objectAtIndex, i))->chars;
247 snprintf(text,
sizeof(text), this->labelFormat, VectorValue(this->values,
double, i));
252 labelWidth = max(labelWidth, width);
255 int minWidth, maxWidth;
257 snprintf(text,
sizeof(text), this->labelFormat, this->min);
260 snprintf(text,
sizeof(text), this->labelFormat, this->max);
263 labelWidth = max(minWidth, maxWidth);
272 View *handle = (
View *) this->handle;
275 if (this->max < this->min) {
292 const SDL_Point points[] = {
293 { frame.x, frame.y + frame.h * 0.5 },
294 { frame.x + frame.w, frame.y + frame.h * 0.5 }
300#pragma mark - Control
311 if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
320 const int x =
event->button.x - frame.x;
324 if (this->delegate.didSetValue) {
325 this->delegate.didSetValue(
this, this->value);
331 else if (event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
333 self->
state &= ~ControlStateHighlighted;
338 else if (event->type == SDL_EVENT_MOUSE_MOTION) {
342 const double fraction = (double) (event->motion.x - frame.x) / (double) frame.w;
345 const double delta = fabs(this->value - value);
346 if (delta > __DBL_EPSILON__) {
349 if (this->delegate.didSetValue) {
350 this->delegate.didSetValue(
this, this->value);
359 if (event->type == SDL_EVENT_KEY_DOWN) {
362 switch (event->key.key) {
374 if (this->delegate.didSetValue) {
375 this->delegate.didSetValue(
this, this->value);
422 assert(self->handle);
464 value = clamp(value, self->
min, self->
max);
467 const double delta = fabs(self->
value - value);
468 if (delta > __DBL_EPSILON__) {
476#pragma mark - Class lifecycle
483 ((ObjectInterface *) clazz->interface)->dealloc =
dealloc;
486 ((ViewInterface *) clazz->interface)->init =
init;
488 ((ViewInterface *) clazz->interface)->render =
render;
490 ((ControlInterface *) clazz->interface)->captureEvent =
captureEvent;
492 ((SliderInterface *) clazz->interface)->formatLabel =
formatLabel;
493 ((SliderInterface *) clazz->interface)->initWithFrame =
initWithFrame;
495 ((SliderInterface *) clazz->interface)->setLabelFormat =
setLabelFormat;
507 clazz = _initialize(&(
const ClassDef) {
510 .instanceSize =
sizeof(
Slider),
511 .interfaceSize =
sizeof(SliderInterface),
@ ControlStateHighlighted
static void sizeCharacters(const Font *self, const char *chars, int *w, int *h)
static Label * initWithText(Label *self, const char *text, Font *font)
View logging facilities via SDL_Log.
#define MVC_LogWarn(fmt,...)
static void addSubview(View *self, View *subview)
static void drawLine(const Renderer *self, const SDL_Point *points, const SDL_Color *color)
static void setValue(Slider *self, double value)
static void bindLabels(const Inlet *inlet, ident obj)
InletBinding for Slider::labels.
static void setLabelFormat(Slider *self, const char *labelFormat)
static View * init(View *self)
static void awakeWithDictionary(View *self, const Dictionary *dictionary)
static bool captureEvent(Control *self, const SDL_Event *event)
static size_t indexOfValue(const Slider *self, double value)
static double valueAfterSteps(const Slider *self, int steps)
static void render(View *self, Renderer *renderer)
static Slider * initWithFrame(Slider *self, const SDL_Rect *frame)
static void dealloc(Object *self)
static void bindValues(const Inlet *inlet, ident obj)
InletBinding Slider::values.
static double fractionOfValue(const Slider *self)
static void initialize(Class *clazz)
static void layoutSubviews(View *self)
static double valueAtFraction(const Slider *self, double fraction)
static void formatLabel(Slider *self)
A Control allowing users to drag a handle to select a numeric value.
static void setText(Text *self, const char *text)
@ InletTypeApplicationDefined
#define MakeInlets(...)
Creates a null-termianted array of Inlets.
#define MakeInlet(name, type, dest, data)
Creates an Inlet with the specified parameters.
static void addClassName(View *self, const char *className)
static bool didReceiveEvent(const View *self, const SDL_Event *event)
static void setNeedsLayout(View *self)
static SDL_Rect renderFrame(const View *self)
static SDL_Rect bounds(const View *self)
Controls are Views which capture and respond to events.
unsigned int state
The bit mask of ControlState.
Inlets enable inbound data binding of View attributes through JSON.
const char * name
The Inlet name, e.g. "alignment".
ident dest
The Inlet destination.
Renderer extends Object with ObjectivelyMVC's UI rendering layer.
A Control allowing users to drag a handle to select a numeric value.
double min
The slider bounds.
Vector * values
An optional Vector of non-linear values.
Array * labels
An optional Array of non-linear labels parallel to values.
View * bar
The slider bar.
Text * label
The Text displaying the current value.
char * labelFormat
The label format, e.g. "%0.01f".
double value
The slider value.
void setValue(Slider *self, double value)
Sets this Slider's value, invalidating its layout and notifying the delegate.
bool snapToStep
Enable snapping the slider handle to the step.
double step
The step to increment on key events.
Text rendered with TrueType fonts.
Font * font
The Font. A fixed-width Font carries a bitmap, and is drawn as one quad per glyph from its glyph shee...
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
ViewPadding padding
The padding.
layoutSubviews(View *self)
Arranges this View's immediate subviews within its (already final) frame.
SDL_Rect frame
The frame, relative to the superview.