1 /**
2  * Mouse demo for dcell.  This demonstrates various forms of input handling.
3  *
4  * Copyright: Copyright 2022 Garrett D'Amore
5  * Authors: Garrett D'Amore
6  * License:
7  *   Distributed under the Boost Software License, Version 1.0.
8  *   (See accompanying file LICENSE or https://www.boost.org/LICENSE_1_0.txt)
9  *   SPDX-License-Identifier: BSL-1.0
10  */
11 module mouse;
12 
13 import std.stdio;
14 import std.string;
15 import std.concurrency;
16 import core.stdc.stdlib;
17 
18 import dcell;
19 
20 void emitStr(Screen s, Coord pos, Style style, dstring str)
21 {
22 	while (str != "")
23 	{
24 		s[pos] = Cell(str[0], style);
25 		str = str[1 .. $];
26 		pos.x++;
27 	}
28 }
29 
30 void order(ref int i1, ref int i2)
31 {
32 	if (i2 < i1)
33 	{
34 		int v = i1;
35 		i1 = i2;
36 		i2 = v;
37 	}
38 }
39 
40 void order(ref Coord c1, ref Coord c2)
41 {
42 	order(c1.x, c2.x);
43 	order(c1.y, c2.y);
44 }
45 
46 void drawBox(Screen s, Coord c1, Coord c2, Style style, dchar fill = ' ')
47 {
48 	order(c1, c2);
49 	Coord pos;
50 	for (pos.x = c1.x; pos.x <= c2.x; pos.x++)
51 	{
52 		s[Coord(pos.x, c1.y)] = Cell(Glyph.horizLine, style);
53 		s[Coord(pos.x, c2.y)] = Cell(Glyph.horizLine, style);
54 	}
55 	for (pos.y = c1.y + 1; pos.y < c2.y; pos.y++)
56 	{
57 		s[Coord(c1.x, pos.y)] = Cell(Glyph.vertLine, style);
58 		s[Coord(c2.x, pos.y)] = Cell(Glyph.vertLine, style);
59 	}
60 	if (c1.y != c2.y && c1.x != c2.x)
61 	{
62 		s[Coord(c1.x, c1.y)] = Cell(Glyph.upperLeftCorner, style);
63 		s[Coord(c2.x, c1.y)] = Cell(Glyph.upperRightCorner, style);
64 		s[Coord(c1.x, c2.y)] = Cell(Glyph.lowerLeftCorner, style);
65 		s[Coord(c2.x, c2.y)] = Cell(Glyph.lowerRightCorner, style);
66 	}
67 	for (pos.y = c1.y + 1; pos.y < c2.y; pos.y++)
68 	{
69 		for (pos.x = c1.x + 1; pos.x < c2.x; pos.x++)
70 		{
71 			s[pos] = Cell(fill, style);
72 		}
73 	}
74 }
75 
76 void drawSelect(Screen s, Coord c1, Coord c2, bool sel)
77 {
78 	order(c1, c2);
79 	Coord pos;
80 	for (pos.y = c1.y; pos.y < c2.y; pos.y++)
81 	{
82 		for (pos.x = c1.x; pos.x < c2.x; pos.x++)
83 		{
84 			if (sel)
85 				s[pos].style.attr |= Attr.reverse;
86 			else
87 				s[pos].style.attr &= ~Attr.reverse;
88 		}
89 	}
90 }
91 
92 void main()
93 {
94 	import std.stdio;
95 
96 	auto s = newScreen();
97 	assert(s !is null);
98 
99 	dstring posFmt = "Mouse: %d, %d";
100 	dstring btnFmt = "Buttons: %s";
101 	dstring keyFmt = "Keys: %s";
102 	dstring pasteFmt = "Paste: [%d] %s";
103 	dstring bStr = "";
104 	dstring kStr = "";
105 	dstring pStr = "";
106 	bool pasting = false;
107 
108 	s.start(thisTid());
109 	s.showCursor(Cursor.hidden);
110 	s.enableMouse(MouseEnable.all);
111 	s.enablePaste(true);
112 	Style white;
113 	white.fg = Color.midnightBlue;
114 	white.bg = Color.lightCoral;
115 	Coord mousePos = Coord(-1, -1);
116 	Coord oldTop = Coord(-1, -1);
117 	Coord oldBot = Coord(-1, -1);
118 	Coord bPos;
119 	int esc = 0;
120 	dchar lb;
121 
122 	for (;;)
123 	{
124 		auto ps = pStr;
125 		if (ps.length > 25)
126 			ps = "..." ~ ps[$ - 23 .. $];
127 		drawBox(s, Coord(1, 1), Coord(42, 7), white);
128 		Coord pos = Coord(3, 2);
129 		emitStr(s, pos, white, "Press ESC twice to exit, C to clear.");
130 		pos.y++;
131 		emitStr(s, pos, white, format(posFmt, mousePos.x, mousePos.y));
132 		pos.y++;
133 		emitStr(s, pos, white, format(btnFmt, bStr));
134 		pos.y++;
135 		emitStr(s, pos, white, format(keyFmt, kStr));
136 		pos.y++;
137 		emitStr(s, pos, white, format(pasteFmt, pStr.length, ps));
138 		s.show();
139 		bStr = "";
140 		Event ev;
141 		receive(
142 			(Event ee) { ev = ee; }
143 		);
144 		Style st;
145 		st.bg = Color.red;
146 		Style up;
147 		up.bg = Color.blue;
148 		up.fg = Color.black;
149 		// clear previous selection
150 		if (oldTop.x >= 0 && oldTop.y >= 0 && oldBot.x >= 0)
151 		{
152 			drawSelect(s, oldTop, oldBot, false);
153 		}
154 		pos = s.size();
155 		pos.x--;
156 		pos.y--;
157 
158 		switch (ev.type)
159 		{
160 		case EventType.resize:
161 			s.resize();
162 			s.sync();
163 			break;
164 		case EventType.paste:
165 			pStr = ev.paste.content;
166 			break;
167 		case EventType.key:
168 			pStr = "";
169 			s[pos] = Cell('K', st);
170 			switch (ev.key.key)
171 			{
172 			case Key.esc:
173 				esc++;
174 				if (esc > 1)
175 				{
176 					s.stop();
177 					exit(0);
178 				}
179 				break;
180 			case Key.ctrlL:
181 				s.sync();
182 				esc = 0;
183 				break;
184 			case Key.rune:
185 				if (ev.key.ch == 'C' || ev.key.ch == 'c')
186 				{
187 					s.clear();
188 				}
189 				esc = 0;
190 				s[pos] = Cell('R', st);
191 				break;
192 			default:
193 				break;
194 			}
195 			kStr = ev.key.toString();
196 			break;
197 		case EventType.mouse:
198 			if (ev.mouse.mod & Modifiers.shift)
199 				bStr ~= " S";
200 			if (ev.mouse.mod & Modifiers.ctrl)
201 				bStr ~= " C";
202 			if (ev.mouse.mod & Modifiers.alt)
203 				bStr ~= " A";
204 			if (ev.mouse.mod & Modifiers.meta)
205 				bStr ~= " M";
206 			if (ev.mouse.btn & Buttons.wheelUp)
207 				bStr ~= " WU";
208 			if (ev.mouse.btn & Buttons.wheelDown)
209 				bStr ~= " WD";
210 			if (ev.mouse.btn & Buttons.wheelLeft)
211 				bStr ~= " WL";
212 			if (ev.mouse.btn & Buttons.wheelRight)
213 				bStr ~= " WR";
214 			// we only want buttons, not wheel events
215 			auto button = ev.mouse.btn;
216 			button &= ~Buttons.wheelUp;
217 			button &= ~Buttons.wheelDown;
218 			button &= ~Buttons.wheelLeft;
219 			button &= ~Buttons.wheelRight;
220 			if (button == Buttons.none)
221 			{
222 				if (oldTop.x >= 0)
223 				{
224 					Style ns = up;
225 					ns.bg = (cast(Color)(lb - '0'));
226 					drawBox(s, oldTop, ev.mouse.pos, ns, lb);
227 					oldTop = Coord(-1, -1);
228 					oldBot = Coord(-1, -1);
229 				}
230 			}
231 			else if (oldTop.x < 0)
232 			{
233 				oldTop = ev.mouse.pos;
234 			}
235 			if (button & Buttons.button1)
236 				bStr ~= " B1";
237 			if (button & Buttons.button2)
238 				bStr ~= " B2";
239 			if (button & Buttons.button3)
240 				bStr ~= " B3";
241 			if (button & Buttons.button4)
242 				bStr ~= " B4";
243 			if (button & Buttons.button5)
244 				bStr ~= " B5";
245 			if (button & Buttons.button6)
246 				bStr ~= " B6";
247 			if (button & Buttons.button7)
248 				bStr ~= " B7";
249 			if (button & Buttons.button8)
250 				bStr ~= " B8";
251 			if (bStr.length > 0)
252 				lb = bStr[$ - 1];
253 			mousePos = ev.mouse.pos;
254 			if (button != Buttons.none)
255 				oldBot = ev.mouse.pos;
256 
257 			s[pos] = Cell('M', st);
258 			break;
259 		default:
260 			s[pos] = Cell('X', st);
261 			break;
262 		}
263 		if (oldTop.x >= 0 && oldBot.x >= 0)
264 		{
265 			drawSelect(s, oldTop, oldBot, true);
266 		}
267 	}
268 }