1 module dinu.mainWindow; 2 3 4 import dinu; 5 6 7 __gshared: 8 9 10 private double em1; 11 12 int em(double mod){ 13 return cast(int)(round(em1*mod)); 14 } 15 16 double eerp(double current, double target, double speed){ 17 auto dir = current > target ? -1 : 1; 18 auto spd = abs(target-current)*speed+speed; 19 spd = spd.min(abs(target-current)).max(0); 20 return current + spd*dir; 21 } 22 23 struct Screen { 24 int x, y, w, h; 25 } 26 27 28 Screen[int] screens(Display* display){ 29 int count; 30 auto screenInfo = XineramaQueryScreens(display, &count); 31 Screen[int] res; 32 foreach(screen; screenInfo[0..count]) 33 res[screen.screen_number] = Screen(screen.x_org, screen.y_org, screen.width, screen.height); 34 XFree(screenInfo); 35 return res; 36 37 } 38 39 40 auto mapKeyToChar(ws.wm.Window window, XKeyEvent* e, void delegate(dchar) cb){ 41 char[25] str; 42 KeySym ks; 43 Status st; 44 size_t l = Xutf8LookupString(window.inputContext, e, str.ptr, 25, &ks, &st); 45 foreach(dchar c; str[0..l]) 46 if(!c.isControl) 47 cb(c); 48 } 49 50 51 class WindowMain: ws.wm.Window { 52 53 ws.wm.Window resultWindow; 54 int padding; 55 int animationY; 56 bool shouldClose; 57 long lastUpdate; 58 double animStart; 59 double scrollCurrent = 0; 60 double selectCurrent = 0; 61 62 Animation windowAnimation; 63 64 //GlContext context; 65 66 this(){ 67 super(1, 1, "dinu", true); 68 draw.setFont(options.font, 12); 69 auto screens = screens(display); 70 if(options.screen !in screens){ 71 "Screen %s does not exist".format(options.screen).writeln; 72 options.screen = screens.keys[0]; 73 } 74 auto screen = screens[options.screen]; 75 em1 = draw.fontHeight*1.2; 76 //context.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 77 //context.enable(GL_BLEND); 78 resize([ 79 options.w ? options.w : screen.w, 80 1.em*(options.lines+1)+0.8.em 81 ]); 82 move([ 83 options.x + screen.x, 84 options.y - size.h 85 ]); 86 show; 87 grabKeyboard; 88 padding = 0.4.em; 89 lastUpdate = (now*1000).lround; 90 windowAnimation = new AnimationExpIn(pos.y, 0, (0.1+size.h/4000.0)*options.animationMove); 91 wm.on([ 92 KeyPress: (XEvent* e){ keyboard(cast(Keyboard.key)XLookupKeysym(&e.xkey,0), true); this.mapKeyToChar(&e.xkey, &keyboard); }, 93 KeyRelease: (XEvent* e) => keyboard(cast(Keyboard.key)XLookupKeysym(&e.xkey,0), false) 94 ]); 95 } 96 97 /+ 98 override void drawInit(){ 99 context = new GlContext(windowHandle); 100 draw = new GlDraw(context); 101 } 102 +/ 103 104 override void resized(int[2] size){ 105 super.resized(size); 106 onDraw; 107 } 108 109 void update(){ 110 auto cur = (now*1000).lround; 111 auto delta = cur-lastUpdate; 112 lastUpdate = cur; 113 int targetY = cast(int)windowAnimation.calculate+options.y; 114 if(windowAnimation.done && shouldClose){ 115 writeln("CLOSE"); 116 super.close; 117 return; 118 }else if(targetY != pos.y){ 119 move([pos.x, targetY]); 120 } 121 auto matches = output.idup; 122 auto selected = commandBuilder.selected < -1 ? -commandBuilder.selected-2 : -1; 123 auto scrollTarget = min(max(0, cast(long)matches.length-cast(long)options.lines), max(0, selected+1-options.lines/2)); 124 if(options.animations > 0){ 125 scrollCurrent = scrollCurrent.eerp(scrollTarget, delta/150.0); 126 selectCurrent = selectCurrent.eerp(commandBuilder.selected, delta/50.0); 127 }else{ 128 scrollCurrent = scrollTarget; 129 selectCurrent = commandBuilder.selected; 130 } 131 } 132 133 override void onDraw(){ 134 if(hidden) 135 return; 136 assert(thread_isMainThread); 137 138 int separator = (size.w*options.ratio).to!int; 139 drawOutput([0, size.h-1.em*options.lines], [size.w, 1.em*options.lines], separator); 140 drawInput([0, 0], [size.w, size.h-1.em*options.lines], separator); 141 super.onDraw; 142 } 143 144 void drawInput(int[2] pos, int[2] size, int sep){ 145 auto paddingVert = 0.2.em; 146 draw.setColor(options.colorBg); 147 draw.rect(pos, size); 148 draw.setColor(options.colorInputBg); 149 draw.rect([sep, pos.y+paddingVert], [size.w - sep*2, 1]); 150 151 // cwd 152 int textY = pos.y + ((size.h - draw.fontHeight)/2.0).lround.to!int; 153 //draw.setColor([1,0,0,0.1]); 154 //draw.rect([0,textY], [size.w, draw.fontHeight]); 155 auto context = getcwd.replace("~".expandTilde, "~").split("/"); 156 auto partAdvance = draw.width(context[$-1]~"/"); 157 draw.setColor(options.colorHint); 158 draw.text([pos.x+sep-partAdvance, textY], context[0..$-1].join("/"), 1.4); 159 if(context.length > 1) 160 draw.text([pos.x+sep-partAdvance+draw.width("/"), textY], "/", 1.4); 161 draw.setColor(options.colorOutput); 162 draw.text([pos.x+sep, textY], context[$-1], 1.4); 163 164 draw.clip([pos.x+sep, pos.y], [size.w - sep*2, size.h]); 165 int width = draw.width(commandBuilder.cursorPart ~ ".."); 166 int offset = -max(0, width-size.w - sep*2); 167 168 // cursor 169 auto selStart = min(commandBuilder.cursor, commandBuilder.cursorStart); 170 auto selEnd = max(commandBuilder.cursor, commandBuilder.cursorStart); 171 int cursorOffset = padding+offset+pos.x+sep+draw.width(commandBuilder.finishedPart); 172 int selpos = cursorOffset+draw.width(commandBuilder.text[0..selEnd].to!string); 173 if(commandBuilder.cursorStart != commandBuilder.cursor){ 174 auto start = cursorOffset+draw.width(commandBuilder.text[0..selStart].to!string); 175 draw.setColor(options.colorHint); 176 draw.rect([start, pos.y+paddingVert*2], [selpos-start, size.y-paddingVert*4]); 177 } 178 int curpos = cursorOffset+draw.width(commandBuilder.text[0..commandBuilder.cursor].to!string); 179 draw.setColor(options.colorInput); 180 draw.rect([curpos, pos.y+paddingVert*2], [1, size.y-paddingVert*4]); 181 182 // input 183 int textStart = offset+pos.x+sep+padding; 184 if(!commandBuilder.commandSelected){ 185 draw.text([textStart, textY], commandBuilder.toString, 0); 186 }else{ 187 auto xoff = textStart+commandBuilder.commandSelected[0].draw(draw, [textStart, textY], false, []); 188 draw.setColor(options.colorInput); 189 foreach(param; commandBuilder.command[1..$]) 190 xoff += draw.text([xoff, textY], ' ' ~ param.to!string, 0); 191 } 192 draw.noclip; 193 //draw.text([10, textY], commandBuilder.toString.expandTilde.buildNormalizedPath, 0); 194 } 195 196 void drawOutput(int[2] pos, int[2] size, int sep){ 197 draw.setColor(options.colorOutputBg); 198 draw.rect(pos, size); 199 auto output = output.idup; 200 auto selected = commandBuilder.selected < -1 ? -commandBuilder.selected-2 : -1; 201 auto start = cast(size_t)scrollCurrent; 202 auto textOffset = ((1.em - draw.fontHeight)/2.0).lround.to!int; 203 if(selectCurrent < -1){ 204 draw.setColor(options.colorHintBg); 205 draw.rect([pos.x+sep, cast(int)(pos.y + size.h*(-2-selectCurrent-scrollCurrent)/cast(double)options.lines)], [size.w-sep*2, 1.em]); 206 } 207 foreach(i, match; output[start..min($, start+options.lines+1)]){ 208 int y = cast(int)(pos.y + size.h*(i-(scrollCurrent-start))/cast(double)options.lines) + textOffset; 209 draw.clip([pos.x, pos.y], [size.w-sep, size.h]); 210 match.draw(draw, [pos.x+sep+padding, y], start+i == selected, []); 211 draw.noclip; 212 debug(Score){ 213 draw.setColor(options.colorInput); 214 draw.text([size.w-sep, y], match.score.to!string); 215 } 216 } 217 if(output.length > 15){ 218 double scrollbarHeight = size.h/(max(1.0, (cast(long)output.length-cast(long)14).log2)); 219 int scrollbarOffset = cast(int)((size.h - scrollbarHeight) * (scrollCurrent/(max(1.0, output.length-15)))); 220 draw.setColor(options.colorHintBg); 221 draw.rect([size.w-sep-0.2.em, scrollbarOffset], [0.2.em, cast(int)scrollbarHeight]); 222 } 223 } 224 225 void showOutput(){ 226 if(hidden) 227 return; 228 options.lines = 15; 229 int height = 1.em*(options.lines+1)+0.8.em-1; 230 resize([size.w, height]); 231 move([pos.x, pos.y-height+size.h]); 232 windowAnimation = new AnimationExpIn(pos.y-height+size.h, options.y, (0.1+size.h/4000.0)*options.animationMove); 233 } 234 235 override void close(){ 236 XUngrabKeyboard(wm.displayHandle, CurrentTime); 237 commandBuilder.destroy; 238 windowAnimation = new AnimationExpOut(pos.y, -size.h, (0.1+size.h/4000.0)*options.animationMove); 239 shouldClose = true; 240 //super.close(); 241 } 242 243 bool[Keyboard.key] keys; 244 245 void keyboard(Keyboard.key key, bool pressed){ 246 keys[key] = pressed; 247 if(!pressed) 248 return; 249 auto control = keys.get(Keyboard.control, false) || keys.get(Keyboard.controlR, false); 250 auto shift = keys.get(Keyboard.shift, false) || keys.get(Keyboard.shiftR, false); 251 if(control) 252 switch(key){ 253 case XK_q: key = XK_Escape; break; 254 case XK_u: commandBuilder.deleteLeft; return; 255 case XK_BackSpace: commandBuilder.deleteWordLeft; return; 256 case XK_Delete: commandBuilder.deleteWordRight; return; 257 case XK_j: commandBuilder.moveLeft; return; 258 case XK_semicolon: commandBuilder.moveRight; return; 259 case XK_V: 260 //case XK_v: XConvertSelection(display, clip, utf8, utf8, handle, CurrentTime); return; 261 case XK_a: commandBuilder.selectAll; return; 262 default: break; 263 } 264 switch(key){ 265 case XK_Escape: .close(); return; 266 case XK_Delete: commandBuilder.delChar; return; 267 case XK_BackSpace: commandBuilder.delBackChar; return; 268 case XK_Left: commandBuilder.moveLeft(control); return; 269 case XK_Right: commandBuilder.moveRight(control); return; 270 case XK_Down: commandBuilder.select(commandBuilder.selected+1); return; 271 case XK_Tab: if(!shift){ 272 commandBuilder.select(commandBuilder.selected+1); return; 273 }else{ 274 goto case XK_Up; 275 } 276 case XK_Up: 277 if(!options.lines && commandBuilder.selected == -1){ 278 showOutput; 279 }else 280 commandBuilder.select(commandBuilder.selected-1); 281 return; 282 case XK_Page_Up: commandBuilder.select(commandBuilder.selected-15); 283 if(commandBuilder.selected < 0) 284 showOutput; 285 break; 286 case XK_Page_Down: commandBuilder.select(commandBuilder.selected+15); break; 287 case XK_Return: 288 case XK_KP_Enter: 289 commandBuilder.run(!control); 290 if(shift && !options.lines){ 291 showOutput; 292 } 293 if(!control && !shift) 294 .close(); 295 return; 296 case Keyboard.shift: 297 case Keyboard.shiftR: commandBuilder.shiftDown = !commandBuilder.shiftDown; break; 298 default: break; 299 } 300 onDraw; 301 } 302 303 void keyboard(dchar key){ 304 commandBuilder.insert(key.to!dstring); 305 onDraw; 306 } 307 308 override void onPaste(string text){ 309 commandBuilder.insert(text.to!dstring); 310 } 311 312 void grabKeyboard(){ 313 foreach(i; 0..100){ 314 if(XGrabKeyboard(display, windowHandle, true, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) 315 return; 316 Thread.sleep(dur!"msecs"(10)); 317 } 318 .close(); 319 assert(0, "cannot grab keyboard"); 320 } 321 322 } 323