e1f1cccce978131a7d211129c3a20440072fc7da
[spectmorph.git] / glui / smlineedit.hh
1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
2
3 #ifndef SPECTMORPH_LINEEDIT_HH
4 #define SPECTMORPH_LINEEDIT_HH
5
6 #include "smdrawutils.hh"
7 #include "smmath.hh"
8
9 namespace SpectMorph
10 {
11
12 struct LineEdit : public Widget
13 {
14 protected:
15   std::string m_text;
16   bool highlight = false;
17
18 public:
19   void
20   set_text (const std::string& new_text)
21   {
22     if (m_text == new_text)
23       return;
24
25     m_text = new_text;
26     update();
27   }
28   std::string
29   text() const
30   {
31     return m_text;
32   }
33   LineEdit (Widget *parent, const std::string& start_text) :
34     Widget (parent),
35     m_text (start_text)
36   {
37   }
38   void
39   draw (const DrawEvent& devent) override
40   {
41     DrawUtils du (devent.cr);
42
43     double space = 2;
44     Color fill_color;
45     if (highlight)
46       fill_color = ThemeColor::MENU_BG;
47
48     Color text_color (1, 1, 1);
49     Color frame_color = ThemeColor::FRAME;
50     if (!recursive_enabled())
51       {
52         text_color = text_color.darker();
53         frame_color = frame_color.darker();
54       }
55
56     du.round_box (0, space, width, height - 2 * space, 1, 5, frame_color, fill_color);
57
58     du.set_color (text_color);
59     du.text (m_text, 10, 0, width - 10, height);
60     double w_ = du.text_width ("_");
61     double tw = du.text_width ("_" + m_text + "_") - 2 * w_; /* also count spaces at start/end */
62     du.round_box (10 + tw + 1, space * 2, w_, height - 4 * space, 1, 0, Color::null(), ThemeColor::SLIDER /* FIXME */ );
63   }
64   bool
65   is_control (uint32 u)
66   {
67     return (u <= 0x1F) || (u >= 0x7F && u <= 0x9f);
68   }
69   static std::string
70   utf8_from_unicode (const std::vector<uint32>& unicode)
71   {
72     std::string utf8 = "";
73     for (auto c : unicode)
74       {
75         char buffer[8] = { 0, };
76         g_unichar_to_utf8 (c, buffer);
77         utf8 += buffer;
78       }
79     return utf8;
80   }
81   static std::vector<uint32>
82   utf8_to_unicode (const std::string& utf8)
83   {
84     gunichar *uc = g_utf8_to_ucs4 (utf8.c_str(), -1, NULL, NULL, NULL);
85     std::vector<uint32> chars;
86     if (uc)
87       {
88         for (size_t i = 0; uc[i]; i++)
89           chars.push_back (uc[i]);
90       }
91     g_free (uc);
92     return chars;
93   }
94   virtual void
95   key_press_event (const PuglEventKey& key_event) override
96   {
97     std::string old_text = m_text;
98
99     if (key_event.filter)
100       {
101         /* multi key sequence -> ignore */
102         return;
103       }
104     if (!is_control (key_event.character) && key_event.utf8[0])
105       {
106         m_text += (const char *) key_event.utf8;
107       }
108     else if ((key_event.character == PUGL_CHAR_BACKSPACE || key_event.character == PUGL_CHAR_DELETE) && !m_text.empty())
109       {
110         // Windows and Linux use backspace, macOS uses delete, so we support both
111         std::vector<uint32> chars = utf8_to_unicode (m_text);
112         if (chars.size())
113           chars.pop_back();
114         m_text = utf8_from_unicode (chars);
115       }
116     else if (key_event.character == 13)
117       {
118         signal_return_pressed();
119       }
120     else if (key_event.character == 27)
121       {
122         signal_esc_pressed();
123       }
124     if (m_text != old_text)
125       {
126         signal_text_changed (m_text);
127         update();
128       }
129   }
130   void
131   enter_event() override
132   {
133     highlight = true;
134     update();
135   }
136   void
137   leave_event() override
138   {
139     highlight = false;
140     update();
141   }
142
143   Signal<std::string> signal_text_changed;
144   Signal<>            signal_return_pressed;
145   Signal<>            signal_esc_pressed;
146 };
147
148 }
149
150 #endif
151