Skip to content

Commit

Permalink
feat(ui): allow filling rectangles and ellipsis
Browse files Browse the repository at this point in the history
This adds an option on top of existing brush types, to allow for filled
shapes in addition to non-filled ones.

Signed-off-by: flow <flowlnlnln@gmail.com>

Closes #120
  • Loading branch information
flowln authored Nov 18, 2022
1 parent 60da549 commit 8ee55f7
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 4 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ text_size=20
text_font=sans-serif
paint_mode=brush
early_exit=false
fill_shape=false
```

- `save_dir` is where swappshots will be saved, can contain env variables, when it does not exist, swappy attempts to create it first, but does not abort if directory creation fails
Expand All @@ -60,6 +61,7 @@ early_exit=false
- `text_font` is the font used to render text, its format is pango friendly
- `paint_mode` is the mode activated at application start (must be one of: brush|text|rectangle|ellipse|arrow|blur, matching is case-insensitive)
- `early_exit` is used to make the application exit after saving the picture or copying it to the clipboard
- `fill_shape` is used to toggle shape filling (for the rectangle and ellipsis tools) on or off upon startup

## Keyboard Shortcuts

Expand All @@ -83,6 +85,7 @@ early_exit=false
- `Minus`: Reduce Stroke Size
- `Plus`: Increase Stroke Size
- `Equal`: Reset Stroke Size
- `f`: Toggle Shape Filling
- `k`: Clear Paints (cannot be undone)

<hr>
Expand Down
1 change: 1 addition & 0 deletions include/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define CONFIG_SAVE_FILENAME_FORMAT_DEFAULT "swappy-%Y%m%d_%H%M%S.png"
#define CONFIG_PAINT_MODE_DEFAULT SWAPPY_PAINT_MODE_BRUSH
#define CONFIG_EARLY_EXIT_DEFAULT false
#define CONFIG_FILL_SHAPE_DEFAULT false

void config_load(struct swappy_state *state);
void config_free(struct swappy_state *state);
3 changes: 3 additions & 0 deletions include/swappy.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,16 @@ struct swappy_state_ui {

GtkButton *line_size;
GtkButton *text_size;

GtkToggleButton *fill_shape;
};

struct swappy_config {
char *config_file;
char *save_dir;
char *save_filename_format;
gint8 paint_mode;
gboolean fill_shape;
gboolean show_panel;
guint32 line_size;
guint32 text_size;
Expand Down
29 changes: 29 additions & 0 deletions res/swappy.glade
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,35 @@
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkToggleButton" id="fill-shape-toggle-button">
<property name="label" translatable="yes">Fill shape</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Toggle shape filling</property>
<property name="always-show-image">True</property>
<signal name="toggled" handler="fill_shape_toggled_handler" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="resize">False</property>
Expand Down
46 changes: 44 additions & 2 deletions src/application.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ static void update_ui_panel_toggle_button(struct swappy_state *state) {
gtk_widget_set_visible(painting_box, toggled);
}

static void update_ui_fill_shape_toggle_button(struct swappy_state *state) {
GtkToggleButton *button = GTK_TOGGLE_BUTTON(state->ui->fill_shape);
gboolean toggled = state->config->fill_shape;

gtk_toggle_button_set_active(button, toggled);
}

void application_finish(struct swappy_state *state) {
g_debug("application finishing, cleaning up");
paint_free_all(state);
Expand Down Expand Up @@ -126,26 +133,32 @@ static void action_set_color_from_custom(struct swappy_state *state) {

static void switch_mode_to_brush(struct swappy_state *state) {
state->mode = SWAPPY_PAINT_MODE_BRUSH;
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), false);
}

static void switch_mode_to_text(struct swappy_state *state) {
state->mode = SWAPPY_PAINT_MODE_TEXT;
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), false);
}

static void switch_mode_to_rectangle(struct swappy_state *state) {
state->mode = SWAPPY_PAINT_MODE_RECTANGLE;
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), true);
}

static void switch_mode_to_ellipse(struct swappy_state *state) {
state->mode = SWAPPY_PAINT_MODE_ELLIPSE;
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), true);
}

static void switch_mode_to_arrow(struct swappy_state *state) {
state->mode = SWAPPY_PAINT_MODE_ARROW;
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), false);
}

static void switch_mode_to_blur(struct swappy_state *state) {
state->mode = SWAPPY_PAINT_MODE_BLUR;
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), false);
}

static void action_stroke_size_decrease(struct swappy_state *state) {
Expand Down Expand Up @@ -202,6 +215,17 @@ static void action_text_size_increase(struct swappy_state *state) {
update_ui_text_size_widget(state);
}

static void action_fill_shape_toggle(struct swappy_state *state, gboolean *toggled) {
// Don't allow changing the state via a shortcut if the button can't be clicked.
if(!gtk_widget_get_sensitive(GTK_WIDGET(state->ui->fill_shape)))
return;

gboolean toggle = (toggled == NULL) ? !state->config->fill_shape : *toggled;
state->config->fill_shape = toggle;

update_ui_fill_shape_toggle_button(state);
}

static void save_state_to_file_or_folder(struct swappy_state *state,
char *file) {
GdkPixbuf *pixbuf = pixbuf_get_from_state(state);
Expand Down Expand Up @@ -409,6 +433,9 @@ void window_keypress_handler(GtkWidget *widget, GdkEventKey *event,
case GDK_KEY_Control_L:
control_modifier_changed(true, state);
break;
case GDK_KEY_f:
action_fill_shape_toggle(state, NULL);
break;
default:
break;
}
Expand Down Expand Up @@ -613,6 +640,12 @@ void text_size_increase_handler(GtkWidget *widget, struct swappy_state *state) {
action_text_size_increase(state);
}

void fill_shape_toggled_handler(GtkWidget *widget, struct swappy_state *state) {
GtkToggleButton *button = GTK_TOGGLE_BUTTON(widget);
gboolean toggled = gtk_toggle_button_get_active(button);
action_fill_shape_toggle(state, &toggled);
}

static void compute_window_size_and_scaling_factor(struct swappy_state *state) {
GdkRectangle workarea = {0};
GdkDisplay *display = gdk_display_get_default();
Expand Down Expand Up @@ -748,6 +781,9 @@ static bool load_layout(struct swappy_state *state) {
state->ui->text_size =
GTK_BUTTON(gtk_builder_get_object(builder, "text-size-button"));

state->ui->fill_shape =
GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "fill-shape-toggle-button"));

state->ui->brush = brush;
state->ui->text = text;
state->ui->rectangle = rectangle;
Expand All @@ -771,22 +807,27 @@ static void set_paint_mode(struct swappy_state *state) {
switch (state->mode) {
case SWAPPY_PAINT_MODE_BRUSH:
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state->ui->brush), true);
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), false);
break;
case SWAPPY_PAINT_MODE_TEXT:
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state->ui->text), true);
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), false);
break;
case SWAPPY_PAINT_MODE_RECTANGLE:
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state->ui->rectangle),
true);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state->ui->rectangle), true);
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), true);
break;
case SWAPPY_PAINT_MODE_ELLIPSE:
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state->ui->ellipse), true);
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), true);
break;
case SWAPPY_PAINT_MODE_ARROW:
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state->ui->arrow), true);
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), false);
break;
case SWAPPY_PAINT_MODE_BLUR:
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(state->ui->blur), true);
gtk_widget_set_sensitive(GTK_WIDGET(state->ui->fill_shape), false);
break;
default:
break;
Expand All @@ -813,6 +854,7 @@ static bool init_gtk_window(struct swappy_state *state) {
update_ui_text_size_widget(state);
update_ui_undo_redo(state);
update_ui_panel_toggle_button(state);
update_ui_fill_shape_toggle_button(state);

return true;
}
Expand Down
14 changes: 14 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ static void print_config(struct swappy_config *config) {
g_info("text_size: %d", config->text_size);
g_info("paint_mode: %d", config->paint_mode);
g_info("early_exit: %d", config->early_exit);
g_info("fill_shape: %d", config->fill_shape);
}

static char *get_default_save_dir() {
Expand Down Expand Up @@ -79,6 +80,7 @@ static void load_config_from_file(struct swappy_config *config,
gchar *text_font = NULL;
gchar *paint_mode = NULL;
gboolean early_exit;
gboolean fill_shape;
GError *error = NULL;

if (file == NULL) {
Expand Down Expand Up @@ -220,6 +222,17 @@ static void load_config_from_file(struct swappy_config *config,
error = NULL;
}

fill_shape = g_key_file_get_boolean(gkf, group, "fill_shape", &error);

if (error == NULL) {
config->fill_shape = fill_shape;
} else {
g_info("fill_shape is missing in %s (%s)", file, error->message);
g_error_free(error);
error = NULL;
}


g_key_file_free(gkf);
}

Expand All @@ -236,6 +249,7 @@ static void load_default_config(struct swappy_config *config) {
config->show_panel = CONFIG_SHOW_PANEL_DEFAULT;
config->paint_mode = CONFIG_PAINT_MODE_DEFAULT;
config->early_exit = CONFIG_EARLY_EXIT_DEFAULT;
config->fill_shape = CONFIG_FILL_SHAPE_DEFAULT;
}

void config_load(struct swappy_state *state) {
Expand Down
5 changes: 4 additions & 1 deletion src/paint.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ void paint_add_temporary(struct swappy_state *state, double x, double y,
paint->content.shape.a = a;
paint->content.shape.w = w;
paint->content.shape.type = type;
paint->content.shape.operation = SWAPPY_PAINT_SHAPE_OPERATION_STROKE;
if (state->config->fill_shape)
paint->content.shape.operation = SWAPPY_PAINT_SHAPE_OPERATION_FILL;
else
paint->content.shape.operation = SWAPPY_PAINT_SHAPE_OPERATION_STROKE;
break;
case SWAPPY_PAINT_MODE_TEXT:
paint->can_draw = false;
Expand Down
14 changes: 13 additions & 1 deletion src/render.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,19 @@ static void render_shape_ellipse(cairo_t *cr, struct swappy_paint_shape shape) {
cairo_scale(cr, x / n, y / n);
cairo_arc(cr, 0, 0, r, 0, 2 * G_PI);
cairo_set_matrix(cr, &save_matrix);
cairo_stroke(cr);

switch (shape.operation) {
case SWAPPY_PAINT_SHAPE_OPERATION_STROKE:
cairo_stroke(cr);
break;
case SWAPPY_PAINT_SHAPE_OPERATION_FILL:
cairo_fill(cr);
break;
default:
cairo_stroke(cr);
break;
}

cairo_close_path(cr);
}

Expand Down
3 changes: 3 additions & 0 deletions swappy.1.scd
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ The following lines can be used as swappy's default:
text_font=sans-serif
paint_mode=brush
early_exit=false
fill_shape=false
```

- *save_dir* is where swappshots will be saved, can contain env variables, when it does not exist, swappy attempts to create it first, but does not abort if directory creation fails
Expand All @@ -75,6 +76,7 @@ The following lines can be used as swappy's default:
- *text_font* is the font used to render text, its format is pango friendly
- *paint_mode* is the mode activated at application start (must be one of: brush|text|rectangle|ellipse|arrow|blur, matching is case-insensitive)
- *early_exit* is used to make the application exit after saving the picture or copying it to the clipboard
- *fill_shape* is used to toggle shape filling (for the rectangle and ellipsis tools) on or off upon startup


# KEY BINDINGS
Expand All @@ -99,6 +101,7 @@ The following lines can be used as swappy's default:
- *Minus*: Reduce Stroke Size
- *Plus*: Increase Stroke Size
- *Equal*: Reset Stroke Size
- *f*: Toggle Shape Filling
- *k*: Clear Paints (cannot be undone)

## MODIFIERS
Expand Down

0 comments on commit 8ee55f7

Please sign in to comment.