1/*
2 * Copyright (c) 2019-2021 Alecaddd (https://alecaddd.com)
3 *
4 * This file is part of Akira.
5 *
6 * Akira is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Akira is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Akira. If not, see <https://www.gnu.org/licenses/>.
18 *
19 * Authored by: Ana Gelez <ana@gelez.xyz>
20 * Authored by: Alessandro "Alecaddd" Castellani <castellani.ale@gmail.com>
21 */
22
23/*
24 * A digit input with a label next to it.
25 */
26public class Akira.Widgets.LinkedInput : Gtk.Grid {
27    public string label { get; construct set; }
28    public string tooltip { get; construct set; }
29    public InputField input_field { get; construct set; }
30
31    /**
32    * Indicates whether the label or the entry should be first
33    */
34    public bool reversed { get; construct set; }
35    public string unit { get; construct set; }
36    public double limit { get; set; }
37    public double value { get; set; }
38    public InputField.Unit icon { get; construct set;}
39    /**
40    * Used to avoid to infinitely updating when value is set externally.
41    */
42    private bool dragging = false;
43    private double dragging_direction = 0;
44    public bool enabled {
45        get {
46            return input_field.entry.sensitive;
47        }
48        set {
49            input_field.entry.sensitive = value;
50        }
51    }
52
53    public LinkedInput (string label, string tooltip = "", string unit = "",
54                        bool reversed = false, double default_val = 0, double limit = 0.0) {
55        Object (
56            label: label,
57            tooltip: tooltip,
58            reversed: reversed,
59            value: default_val,
60            limit: limit,
61            unit: unit
62        );
63    }
64
65    construct {
66        valign = Gtk.Align.CENTER;
67        hexpand = true;
68        get_style_context ().add_class (Gtk.STYLE_CLASS_LINKED);
69
70        var event_box = new Gtk.EventBox ();
71        event_box.event.connect (handle_event);
72
73        var entry_label = new Gtk.Label (label);
74        entry_label.get_style_context ().add_class ("entry-label");
75        entry_label.halign = Gtk.Align.CENTER;
76        entry_label.width_request = 20;
77        entry_label.hexpand = false;
78        entry_label.tooltip_text = tooltip;
79
80        switch (unit) {
81            case "#":
82                icon = InputField.Unit.HASH;
83            break;
84            case "%":
85                icon = InputField.Unit.PERCENTAGE;
86            break;
87            case "px":
88                icon = InputField.Unit.PIXEL;
89            break;
90            case "°":
91                icon = InputField.Unit.DEGREES;
92            break;
93            default:
94                icon = InputField.Unit.PIXEL;
95            break;
96        }
97
98        input_field = new Widgets.InputField (icon, 7, true, false);
99        bind_property (
100            "value", input_field.entry, "value",
101            BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE);
102
103        event_box.add (entry_label);
104
105        if (reversed) {
106            attach (input_field, 0, 0);
107            attach (event_box, 1, 0);
108        } else {
109            attach (event_box, 0, 0);
110            attach (input_field, 1, 0);
111        }
112    }
113
114    public bool handle_event (Gdk.Event event) {
115        if (!input_field.entry.sensitive) {
116            return false;
117        }
118
119        if (event.type == Gdk.EventType.ENTER_NOTIFY) {
120            set_cursor_from_name ("ew-resize");
121        }
122
123        if (event.type == Gdk.EventType.LEAVE_NOTIFY) {
124            set_cursor (Gdk.CursorType.ARROW);
125        }
126
127        if (event.type == Gdk.EventType.BUTTON_PRESS) {
128            dragging = true;
129        }
130
131        if (event.type == Gdk.EventType.BUTTON_RELEASE) {
132            dragging = false;
133            dragging_direction = 0;
134        }
135
136        if (event.type == Gdk.EventType.MOTION_NOTIFY && dragging) {
137            if (dragging_direction == 0) {
138                dragging_direction = event.motion.x;
139            }
140
141            if (dragging_direction > event.motion.x || event.motion.x_root == 0) {
142                input_field.entry.spin (Gtk.SpinType.STEP_BACKWARD, 1);
143                dragging_direction = event.motion.x;
144            } else {
145                input_field.entry.spin (Gtk.SpinType.STEP_FORWARD, 1);
146                dragging_direction = event.motion.x;
147            }
148        }
149
150        return false;
151    }
152
153    private void set_cursor (Gdk.CursorType cursor_type) {
154        var cursor = new Gdk.Cursor.for_display (Gdk.Display.get_default (), cursor_type);
155        get_window ().set_cursor (cursor);
156    }
157
158    private void set_cursor_from_name (string name) {
159        var cursor = new Gdk.Cursor.from_name (Gdk.Display.get_default (), name);
160        get_window ().set_cursor (cursor);
161    }
162}
163