-
Notifications
You must be signed in to change notification settings - Fork 2
/
layouts.fnl
162 lines (142 loc) · 4.59 KB
/
layouts.fnl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
(local atom (require :lib.atom))
(local {:concat concat
:slice slice
:merge merge} (require :lib.functional))
(local fu hs.fnutils)
"
Window Layouts
- Defines common layouts like full screen, left half, right half, left
two-thirds, and right one-third.
- When a layout is activated it will try to apply it to the current window on
the current screen.
- When a layout is repeated to the same window it cycles screens
- We store prev window, layout, screen index, and window frame dimensions to
know when to apply it to current screen versus next screen.
- By tracking the frame dimensions we can determine if the window was adjusted
outside of the layouts system without any complex watching systems.
"
(local state (atom.new
{:index 0
:cell ""
:window {}
:frame ""}))
(fn update
[update-data]
"
Update the state atom with a partial update
Takes the new partial table data to merge into current state
Returns previous state
"
(atom.swap! state (fn [current-data]
(merge current-data update-data))))
(fn win->frame-str
[window]
"
Serialize a window's geometry frame into a string for fast comparison
Takes a hs.window instance
Returns a string serializing the geometry rectangle measurements
"
(let [rect (: window :frame)]
rect.string))
(fn next-screen-index
[idx total]
"
Calculate the next screen index. Wraps to first screen index.
Note lua is 1-index based
Takes the current screen index and the total number of screens.
Returns the next screen index.
"
(let [idx (+ idx 1)]
(if (<= idx total)
idx
1)))
(fn current-screen-index
[screen screens]
"
Get current screen index
Takes the current screen and a table of screens
Returns the index number of the current screen
"
(fu.indexOf screens screen))
(fn current-state
[cell]
"
Return an updated state-like table to perform all our ready side-effects
Takes the target grid cell string
Returns a table. The cell, window, and frame are used to compare against the
previous state while screens and screen are used for calculating the next
screen index.
"
(let [window (hs.window.frontmostWindow)
frame (win->frame-str window)]
{:cell cell
:window window
:frame frame
:screens (hs.screen.allScreens)
:screen (hs.screen.mainScreen)}))
(fn repeated-update?
[prev current]
"
Determine if the user is repeating the same layout as they applied previously.
We also use the frame string to see if the window was adjusted since the
last layout. This predicate is used to determine if the window should be moved
to the next screen or current frame to begin the cycle if repeated.
Takes the previous state table from (atom.deref state) and the current-state
table.
Returns true or false if cell, window, and frame string matches between prev
and current states.
"
(and (= prev.cell current.cell)
(= prev.window current.window)
(= prev.frame current.frame)))
(fn get-cell
[cell screen]
(let [rect (hs.grid.getCell cell screen)]
rect))
(fn grid-resize
[cell]
"
Main function for positioning a window to a grid layout. Gets state, resizes
and repositions a window, then updates the stotred state to comapre on next
layout that is triggered.
It's advised to make any custom layouts wrap this function.
Takes grid cell dimensions like \"x, y wxh\"
Returns previous state table
"
(let [prev (atom.deref state)
prev-duration hs.window.animationDuration
current (current-state cell)
index (if (repeated-update? prev current)
(next-screen-index prev.index (length current.screens))
(current-screen-index current.screen current.screens))
next-screen (. current.screens index)]
(tset hs.window "animationDuration" 0)
(hs.grid.set current.window cell next-screen)
(update {:index index
:cell cell
:window current.window
:frame (win->frame-str current.window)})
(hs.timer.doAfter 0.5 (fn []
(update {:frame (win->frame-str current.window)})
(tset hs.window "animationDuration" prev-duration)))))
(fn full-size
[]
(grid-resize "0, 0 8x2"))
(fn left-half
[]
(grid-resize "0, 0 4x2"))
(fn right-half
[]
(grid-resize "4,0 4x2"))
(fn left-big
[]
(grid-resize "0,0 6x2"))
(fn right-small
[]
(grid-resize "6,0 2x2"))
{:grid-resize grid-resize
:full-size full-size
:left-half left-half
:right-half right-half
:left-big left-big
:right-small right-small}