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: Alessandro "Alecaddd" Castellani <castellani.ale@gmail.com>
20 */
21
22public class Akira.Widgets.InputField : Gtk.EventBox {
23    public Gtk.SpinButton entry { get; construct set; }
24
25    public int chars { get; construct set; }
26    public bool rtl { get; construct set; }
27    public bool icon_right { get; construct set; }
28    public Unit unit { get; construct set; }
29    public string? icon { get; set; }
30
31    public double step { get; set; default = 1; }
32
33    public enum Unit {
34        PIXEL,
35        HASH,
36        PERCENTAGE,
37        DEGREES,
38        NONE
39    }
40
41    public InputField (
42        Unit unit,
43        int chars,
44        bool icon_right = false,
45        bool rtl = false) {
46        Object (
47            unit: unit,
48            chars: chars,
49            icon_right: icon_right,
50            rtl: rtl
51        );
52    }
53
54    construct {
55        valign = Gtk.Align.CENTER;
56
57        entry = new Gtk.SpinButton.with_range (0, 100, step);
58        entry.hexpand = true;
59        entry.width_chars = chars;
60        entry.sensitive = false;
61
62        entry.key_press_event.connect (handle_key_press);
63        entry.scroll_event.connect (handle_scroll_event);
64        entry.focus_in_event.connect (handle_focus_in);
65        entry.focus_out_event.connect (handle_focus_out);
66
67        switch (unit) {
68            case Unit.HASH:
69                icon = "input-hash-symbolic";
70                break;
71            case Unit.PERCENTAGE:
72                icon = "input-percentage-symbolic";
73                break;
74            case Unit.PIXEL:
75                icon = "input-pixel-symbolic";
76                break;
77            case Unit.DEGREES:
78                icon = "input-degrees-symbolic";
79                break;
80            default:
81                icon = null;
82                break;
83        }
84
85        if (icon != null) {
86            if (icon_right) {
87                entry.get_style_context ().add_class ("input-icon-right");
88                entry.secondary_icon_name = icon;
89                entry.secondary_icon_sensitive = false;
90                entry.secondary_icon_activatable = false;
91            } else {
92                entry.get_style_context ().add_class ("input-icon-left");
93                entry.primary_icon_name = icon;
94                entry.primary_icon_sensitive = false;
95                entry.primary_icon_activatable = false;
96            }
97        }
98
99        if (rtl) {
100            entry.xalign = 1.0f;
101        }
102
103        add (entry);
104    }
105
106    public void set_range (double min_value, double max_value) {
107        entry.set_range (min_value, max_value);
108    }
109
110    private bool handle_key_press (Gdk.EventKey event) {
111        // Arrow UP
112        if (event.keyval == Gdk.Key.Up && (event.state & Gdk.ModifierType.SHIFT_MASK) > 0) {
113            entry.spin (Gtk.SpinType.STEP_FORWARD, 10);
114            return true;
115        }
116
117        // Arrow DOWN
118        if (event.keyval == Gdk.Key.Down && (event.state & Gdk.ModifierType.SHIFT_MASK) > 0) {
119            entry.spin (Gtk.SpinType.STEP_BACKWARD, 10);
120            return true;
121        }
122
123        // Enter or Escape
124        if (event.keyval == Gdk.Key.Return || event.keyval == Gdk.Key.Escape) {
125            Akira.Window window = get_toplevel () as Akira.Window;
126            window.event_bus.set_focus_on_canvas ();
127            return true;
128        }
129
130        return false;
131    }
132
133    private bool handle_scroll_event (Gdk.EventScroll event) {
134        // If the input field is not focused, don't change the value.
135        if (!entry.has_focus) {
136            return true;
137        }
138        return false;
139    }
140
141    private bool handle_focus_in (Gdk.EventFocus event) {
142        Akira.Window window = get_toplevel () as Akira.Window;
143        if (!(window is Akira.Window)) {
144            return true;
145        }
146        window.event_bus.disconnect_typing_accel ();
147
148        return false;
149    }
150
151    private bool handle_focus_out (Gdk.EventFocus event) {
152        Akira.Window window = get_toplevel () as Akira.Window;
153        if (!(window is Akira.Window)) {
154            return true;
155        }
156        window.event_bus.connect_typing_accel ();
157
158        return false;
159    }
160}
161