Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scrollwheel and doubleclick support for levels module #14

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 119 additions & 36 deletions src/iop/levels.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ void gui_init(struct dt_iop_module_t *self)
G_CALLBACK (dt_iop_levels_motion_notify), self);
g_signal_connect (G_OBJECT (c->area), "leave-notify-event",
G_CALLBACK (dt_iop_levels_leave_notify), self);
g_signal_connect (G_OBJECT (c->area), "scroll-event",
G_CALLBACK (dt_iop_levels_scroll), self);
}

void gui_cleanup(struct dt_iop_module_t *self)
Expand Down Expand Up @@ -397,6 +399,64 @@ static gboolean dt_iop_levels_expose(GtkWidget *widget, GdkEventExpose *event, g
return TRUE;
}

/**
* Move handler_move to new_pos, storing the value in handles,
* while keeping new_pos within a valid range
* and preserving the ratio between the three handles.
*
* @param handle_move Handle to move
* @param new_pow New position (0..1)
* @param levels Pointer to dt_iop_levels_params->levels.
* @param drag_start_percentage Ratio between handle 1, 2 and 3.
*
* @return TRUE if the marker were given a new position. FALSE otherwise.
*/
static void dt_iop_levels_move_handle(int handle_move, float new_pos, float *levels, float drag_start_percentage)
{
float min_x = 0;
float max_x = 1;

if ((handle_move < 0) || handle_move > 2)
return;

if (levels == NULL)
return;

// Determining the minimum and maximum bounds for the drag handles
switch(handle_move)
{
case 0:
max_x = fminf(levels[2] - (0.05 / drag_start_percentage),
1);
max_x = fminf((levels[2] * (1 - drag_start_percentage) - 0.05)
/ (1 - drag_start_percentage),
max_x);
break;

case 1:
min_x = levels[0] + 0.05;
max_x = levels[2] - 0.05;
break;

case 2:
min_x = fmaxf((0.05 / drag_start_percentage) + levels[0],
0);
min_x = fmaxf((levels[0] * (1 - drag_start_percentage) + 0.05)
/ (1 - drag_start_percentage),
min_x);
break;
}

levels[handle_move] =
fminf(max_x, fmaxf(min_x, new_pos));

if(handle_move != 1)
levels[1] = levels[0] + (drag_start_percentage
* (levels[2] - levels[0]));

}


static gboolean dt_iop_levels_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
Expand All @@ -418,40 +478,7 @@ static gboolean dt_iop_levels_motion_notify(GtkWidget *widget, GdkEventMotion *e
{
const float mx = (CLAMP(event->x - inset, 0, width)) / (float)width;

float min_x = 0;
float max_x = 1;

// Determining the minimum and maximum bounds for the drag handles
switch(c->handle_move)
{
case 0:
max_x = fminf(p->levels[2] - (0.05 / c->drag_start_percentage),
1);
max_x = fminf((p->levels[2] * (1 - c->drag_start_percentage) - 0.05)
/ (1 - c->drag_start_percentage),
max_x);
break;

case 1:
min_x = p->levels[0] + 0.05;
max_x = p->levels[2] - 0.05;
break;

case 2:
min_x = fmaxf((0.05 / c->drag_start_percentage) + p->levels[0],
0);
min_x = fmaxf((p->levels[0] * (1 - c->drag_start_percentage) + 0.05)
/ (1 - c->drag_start_percentage),
min_x);
break;
}

p->levels[c->handle_move] =
fminf(max_x, fmaxf(min_x, mx));

if(c->handle_move != 1)
p->levels[1] = p->levels[0] + (c->drag_start_percentage
* (p->levels[2] - p->levels[0]));
dt_iop_levels_move_handle(c->handle_move, mx, p->levels, c->drag_start_percentage);
}
dt_dev_add_history_item(darktable.develop, self, TRUE);
}
Expand All @@ -477,14 +504,36 @@ static gboolean dt_iop_levels_motion_notify(GtkWidget *widget, GdkEventMotion *e
return TRUE;
}

/** Reset the module and redraws the gui */
static void dt_iop_levels_reset(dt_iop_module_t *self)
{
dt_iop_levels_gui_data_t *c = (dt_iop_levels_gui_data_t *)self->gui_data;
dt_iop_levels_params_t *p = (dt_iop_levels_params_t *)self->params;

p->levels[0] = 0;
p->levels[1] = 0.5;
p->levels[2] = 1;
p->levels_preset = 0;

c->drag_start_percentage = 0.5;

dt_dev_add_history_item(darktable.develop, self, TRUE);
gtk_widget_queue_draw(self->widget);
}

static gboolean dt_iop_levels_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
// set active point
if(event->button == 1)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_levels_gui_data_t *c = (dt_iop_levels_gui_data_t *)self->gui_data;
c->dragging = 1;

if(event->type == GDK_2BUTTON_PRESS) {
dt_iop_levels_reset(self);
} else {
dt_iop_levels_gui_data_t *c = (dt_iop_levels_gui_data_t *)self->gui_data;
c->dragging = 1;
}
return TRUE;
}
return FALSE;
Expand All @@ -502,3 +551,37 @@ static gboolean dt_iop_levels_button_release(GtkWidget *widget, GdkEventButton *
return FALSE;
}

static gboolean dt_iop_levels_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
{
dt_iop_module_t *self = (dt_iop_module_t *)user_data;
dt_iop_levels_gui_data_t *c = (dt_iop_levels_gui_data_t *)self->gui_data;
dt_iop_levels_params_t *p = (dt_iop_levels_params_t *)self->params;

const float interval = 0.002; // Distance moved for each scroll event
gboolean updated = FALSE;
float new_position = 0;

if (c->dragging) {
return FALSE;
}

if(event->direction == GDK_SCROLL_UP)
{
new_position = p->levels[c->handle_move] + interval;
updated = TRUE;
}
else if(event->direction == GDK_SCROLL_DOWN)
{
new_position = p->levels[c->handle_move] - interval;
updated = TRUE;
}

if (updated) {
dt_iop_levels_move_handle(c->handle_move, new_position,
p->levels, c->drag_start_percentage);
dt_dev_add_history_item(darktable.develop, self, TRUE);
return TRUE;
}

return FALSE;
}
3 changes: 3 additions & 0 deletions src/iop/levels.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,13 @@ void gui_cleanup (struct dt_iop_module_t *self);

void process (struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, void *i, void *o, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out);

static void dt_iop_levels_reset(dt_iop_module_t *self);

static gboolean dt_iop_levels_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data);
static gboolean dt_iop_levels_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data);
static gboolean dt_iop_levels_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
static gboolean dt_iop_levels_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
static gboolean dt_iop_levels_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data);
static gboolean dt_iop_levels_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer user_data);

#endif