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 }