diff --git a/src/nimwin.nim b/src/nimwin.nim index 37fbc05..98bb487 100644 --- a/src/nimwin.nim +++ b/src/nimwin.nim @@ -1,5 +1,5 @@ import x11/xlib, x11/xutil, x11/x, x11/keysym -import threadpool, osproc, tables, sequtils, posix, strformat, os, sugar, options, strutils +import threadpool, osproc, tables, sequtils, posix, strformat, os, sugar, options, strutils, algorithm var root : TWindow @@ -48,6 +48,59 @@ type Window = ref object of RootObj screen : PScreen props : seq[WinProp] +type Zipper[T] = tuple[ + lhs: seq[T], + rhs: seq[T] +] + +proc zipperFocus[T](zipper: Zipper[T]) : Option[T] = + if zipper.rhs.len > 0: + some(zipper.rhs[0]) + else: + none(T) + +proc zipperMove[T](zipper: Zipper[T], direction: string) : Zipper[T] = + # This implements a "zipper" data structure + # A zipper is a data structure with a "focus" + # i.e. a pointer into a particular position + # in this case we have a zipper of a list, so we have + # a list with a focus that lets us move left or right + # and it will wrap around to the other side in either direction + # + # This function will always allocate a new structure (or leave it untouched) + # and won't mutate the original, as this is an immutable data structure + + if zipper.rhs.len == 0 and zipper.lhs.len == 0: + return zipper + + if direction == "right" and zipper.rhs.len == 1: + result.lhs = @[] + result.rhs = zipper.lhs.reversed & zipper.rhs # append head(rhs) to lhs + return + + if direction == "right": + result.lhs = @[zipper.rhs[0]] & zipper.lhs # cons head(rhs) onto lhs + result.rhs = zipper.rhs[1..^1] # drop head of rhs + + if direction == "left" and zipper.lhs.len == 0: + result.lhs = zipper.rhs.reversed[1..^1] # make lhs = tail of rhs + result.rhs = @[zipper.rhs.reversed[0]] # make the focus be the last item in the rhs + return + + if direction == "left": + result.lhs = zipper.lhs[1..^1] # drop the head of the lhs + result.rhs = @[zipper.lhs[0]] & zipper.rhs # move the focus left + +proc zipperInsert[T](zipper: Zipper[T], item: T) : Zipper[T] = + # insert a new item before as the current focus + result.lhs = zipper.lhs + result.rhs = @[item] & zipper.rhs + +proc zipperRemove[T](zipper: Zipper[T], item: T) : Zipper[T] = + # find and delete an item in the zipper + result.lhs = filter(zipper.lhs, (x) => x != item) + result.rhs = filter(zipper.rhs, (x) => x != item) + proc unpackPropValue(typeFormat : int, nItems : int, buf : ptr cuchar) : seq[uint] = @@ -400,6 +453,17 @@ when isMainModule: var openProcesses = initTable[int, Process]() # hashset of processes + var windowZipper : Zipper[TWindow] + + # When a window is opened: goes on the top of the rhs stack, focus is moved to it + # When a window is destroyed: + # if it is the current focus: + # remove it from the zipper and focus on the next rhs window, or move the the start of the lhs, or do nothing if no other windows + # if it is not the current focus: + # remove it from the zipper, and keep the current focus + # When focus is changed: + # simply move the focus pointer right, wrapping around if necessary + discard XSetErrorHandler(handleBadWindow) discard XSetIOErrorHandler(handleIOError)