Class: Fatty::PopUpSession
Constant Summary
collapse
- MAX_WIDTH =
120
- DEFAULT_HEIGHT =
12
- MIN_LIST_H =
3
- MAX_LIST_H =
20
- MARGIN =
2
- SELECTED_GUTTER =
'[X] '
- UNSELECTED_GUTTER =
'[ ] '
Instance Attribute Summary collapse
Attributes inherited from ModalSession
#win
Attributes inherited from Session
#counter, #keymap, #terminal, #views
Instance Method Summary
collapse
-
#accept_selection ⇒ Object
-
#counts ⇒ Object
-
#geometry(cols:, rows:) ⇒ Object
Return the outer width and height of the window for this modal, including any padding and borders.
-
#gutter_for(item:, selected:) ⇒ Object
-
#handle_action(action, args, event:) ⇒ Object
-
#init(terminal:) ⇒ Object
Framework and Session Hooks.
-
#initialize(source:, title: nil, message: nil, prompt: "> ", keymap: Keymaps.emacs, matcher: nil, order: :as_given, kind: nil, selection: :preserve, initial_query: nil, selection_mode: :single, validate_unique_labels: false) ⇒ PopUpSession
constructor
API: - source: Proc that returns the candidate list.
-
#keymap_contexts ⇒ Object
-
#matching_count ⇒ Object
-
#move_selected_by(delta) ⇒ Object
-
#refresh_displayed_items ⇒ Object
-
#refresh_items ⇒ Object
-
#refresh_items_if_query_changed ⇒ Object
-
#scroll_start(list_h:) ⇒ Object
Renderer calls this to determine which slice of items to display.
-
#selected_count ⇒ Object
-
#selected_item ⇒ Object
-
#selected_item_label?(item) ⇒ Boolean
-
#selected_labels ⇒ Object
-
#showing_count ⇒ Object
-
#toggle_selected_current! ⇒ Object
-
#toggle_selected_item!(item) ⇒ Object
-
#total_count ⇒ Object
Count methods for display to user.
-
#view(screen:, renderer:) ⇒ Object
#clamp_height, #clamp_width, #close, #handle_resize, #max_height, #max_width, #rebuild_windows!
Methods inherited from Session
#add_view, #close, #handle_resize, #inspect, #persist!, #resolve_action, #tick, #update
Methods included from Actionable
included
Constructor Details
#initialize(source:, title: nil, message: nil, prompt: "> ", keymap: Keymaps.emacs, matcher: nil, order: :as_given, kind: nil, selection: :preserve, initial_query: nil, selection_mode: :single, validate_unique_labels: false) ⇒ PopUpSession
API:
- source: Proc that returns the candidate list. May accept (query) or be arity 0.
- matcher: Proc (item, query) -> truthy. Defaults to substring match.
- order: :as_given (default) or :reverse (presentation order).
- selection: :preserve (default), :top, :bottom (how selection behaves after refresh).
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
|
# File 'lib/fatty/session/popup_session.rb', line 23
def initialize(
source:,
title: nil,
message: nil,
prompt: "> ",
keymap: Keymaps.emacs,
matcher: nil,
order: :as_given,
kind: nil,
selection: :preserve,
initial_query: nil,
selection_mode: :single,
validate_unique_labels: false
)
super(keymap: keymap)
@source = source
@title = title&.to_s
@message = message&.to_s
@prompt = Prompt.ensure(prompt)
@matcher = matcher || method(:default_matcher)
@order = order.to_sym
@kind = kind&.to_sym
@selection = selection.to_sym
@selection_mode = selection_mode.to_sym
@validate_unique_labels = !!validate_unique_labels
@field = InputField.new(prompt: @prompt)
text = initial_query.to_s
@field.buffer.replace(text) unless text.empty?
@items = []
@filtered = []
@displayed = []
@selected = 0
@selected_labels = {}
@last_query = nil
@scroll_start = 0
end
|
Instance Attribute Details
#displayed ⇒ Object
Returns the value of attribute displayed.
7
8
9
|
# File 'lib/fatty/session/popup_session.rb', line 7
def displayed
@displayed
end
|
#field ⇒ Object
Returns the value of attribute field.
7
8
9
|
# File 'lib/fatty/session/popup_session.rb', line 7
def field
@field
end
|
#filtered ⇒ Object
Returns the value of attribute filtered.
7
8
9
|
# File 'lib/fatty/session/popup_session.rb', line 7
def filtered
@filtered
end
|
#message ⇒ Object
Returns the value of attribute message.
7
8
9
|
# File 'lib/fatty/session/popup_session.rb', line 7
def message
@message
end
|
#selected ⇒ Object
Returns the value of attribute selected.
7
8
9
|
# File 'lib/fatty/session/popup_session.rb', line 7
def selected
@selected
end
|
#title ⇒ Object
Returns the value of attribute title.
7
8
9
|
# File 'lib/fatty/session/popup_session.rb', line 7
def title
@title
end
|
Instance Method Details
#accept_selection ⇒ Object
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
|
# File 'lib/fatty/session/popup_session.rb', line 197
def accept_selection
if multi_select?
payload = (selected_result_hash)
else
item = selected_item
query = @field.buffer.text.to_s
return [] if item.nil? && query.empty?
item = query if item.nil?
payload = (item)
end
[
[:terminal, :send_modal_owner, [:cmd, :popup_result, payload]],
[:terminal, :pop_modal]
]
end
|
#counts ⇒ Object
325
326
327
328
329
330
331
332
|
# File 'lib/fatty/session/popup_session.rb', line 325
def counts
{
total: total_count,
selected: selected_count,
matching: matching_count,
showing: showing_count
}
end
|
#geometry(cols:, rows:) ⇒ Object
Return the outer width and height of the window for this modal,
including any padding and borders.
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
# File 'lib/fatty/session/popup_session.rb', line 88
def geometry(cols:, rows:)
max_w = max_width(cols: cols, margin: MARGIN, min_width: 10)
max_h = max_height(rows: rows, margin: MARGIN, min_height: 5)
desired_list_h = @filtered.length.clamp(MIN_LIST_H, MAX_LIST_H)
height = clamp_height(
desired_list_h + ,
max_height: max_h,
min_height: 6,
)
width = clamp_width(
MAX_WIDTH,
max_width: max_w,
)
[width, height]
end
|
#gutter_for(item:, selected:) ⇒ Object
223
224
225
226
227
228
229
|
# File 'lib/fatty/session/popup_session.rb', line 223
def gutter_for(item:, selected:)
if multi_select?
selected_item_label?(item) ? SELECTED_GUTTER : UNSELECTED_GUTTER
else
' '
end
end
|
#handle_action(action, args, event:) ⇒ Object
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
# File 'lib/fatty/session/popup_session.rb', line 172
def handle_action(action, args, event:)
env = action_env(event: event)
if Fatty::Actions.lookup(action)&.fetch(:on) == :session
Fatty::Actions.call(action, env, *args)
else
@field.act_on(action, *args, env: env)
refresh_items_if_query_changed
ensure_scroll_visible
notify_owner(:popup_changed)
end
rescue ActionError => e
Fatty.error("PopUpSession#handle_action: ActionError #{e.message}", tag: :session)
[]
end
|
#init(terminal:) ⇒ Object
Framework and Session Hooks
67
68
69
70
71
|
# File 'lib/fatty/session/popup_session.rb', line 67
def init(terminal:)
refresh_items
rebuild_windows!
notify_owner(:popup_changed)
end
|
#keymap_contexts ⇒ Object
73
74
75
76
77
|
# File 'lib/fatty/session/popup_session.rb', line 73
def keymap_contexts
contexts = [:popup, :text]
contexts.unshift(:popup_multi) if multi_select?
contexts
end
|
#matching_count ⇒ Object
309
310
311
|
# File 'lib/fatty/session/popup_session.rb', line 309
def matching_count
@filtered.length
end
|
#move_selected_by(delta) ⇒ Object
188
189
190
191
192
193
194
195
|
# File 'lib/fatty/session/popup_session.rb', line 188
def move_selected_by(delta)
return if @displayed.empty?
msg = "PopUpSession#move_selected_by before: selected=#{@selected.inspect} delta=#{delta} len=#{@displayed.length}"
Fatty.debug(msg)
@selected = ((@selected || 0) + delta) % @displayed.length
Fatty.debug("PopUpSession#move_selected_by after: selected=#{@selected.inspect}")
end
|
#refresh_displayed_items ⇒ Object
240
241
242
243
244
245
246
247
248
249
250
251
252
|
# File 'lib/fatty/session/popup_session.rb', line 240
def refresh_displayed_items
if multi_select?
selected_missing =
@items.select do |item|
label = item_label(item)
selected_label?(label) && !@filtered.include?(item)
end
@displayed = selected_missing + @filtered
else
@displayed = @filtered.dup
end
end
|
#refresh_items ⇒ Object
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
|
# File 'lib/fatty/session/popup_session.rb', line 254
def refresh_items
q = @field.buffer.text.to_s
@items = Array(call_source(q))
apply_order!
validate_unique_labels!(@items) if @validate_unique_labels
matcher = @matcher || method(:default_matcher)
@filtered =
if q.empty?
@items
else
@items.select { |e| matcher.call(e, q) }
end
refresh_displayed_items
apply_selection_policy!
end
|
#refresh_items_if_query_changed ⇒ Object
231
232
233
234
235
236
237
238
|
# File 'lib/fatty/session/popup_session.rb', line 231
def refresh_items_if_query_changed
q = @field.buffer.text.to_s
return if q == @last_query
@last_query = q.dup.freeze
Fatty.debug("popup query changed", tag: :popup, q: q, last: @last_query)
refresh_items
end
|
Renderer calls this to determine which slice of items to display.
274
275
276
277
278
279
280
281
|
# File 'lib/fatty/session/popup_session.rb', line 274
def scroll_start(list_h:)
max_start = @displayed.length - list_h
max_start = 0 if max_start < 0
@scroll_start = 0 if @scroll_start < 0
@scroll_start = max_start if @scroll_start > max_start
@scroll_start
end
|
#selected_count ⇒ Object
313
314
315
316
317
318
319
|
# File 'lib/fatty/session/popup_session.rb', line 313
def selected_count
if multi_select?
@selected_labels.length
else
selected_item ? 1 : 0
end
end
|
#selected_item ⇒ Object
215
216
217
|
# File 'lib/fatty/session/popup_session.rb', line 215
def selected_item
@displayed[@selected]
end
|
#selected_item_label?(item) ⇒ Boolean
219
220
221
|
# File 'lib/fatty/session/popup_session.rb', line 219
def selected_item_label?(item)
selected_label?(item_label(item))
end
|
#selected_labels ⇒ Object
334
335
336
|
# File 'lib/fatty/session/popup_session.rb', line 334
def selected_labels
@selected_labels.keys
end
|
#showing_count ⇒ Object
321
322
323
|
# File 'lib/fatty/session/popup_session.rb', line 321
def showing_count
[@displayed.length - scroll_start(list_h: ), ].min
end
|
#toggle_selected_current! ⇒ Object
283
284
285
286
287
288
|
# File 'lib/fatty/session/popup_session.rb', line 283
def toggle_selected_current!
item = selected_item
return unless item
toggle_selected_item!(item)
end
|
#toggle_selected_item!(item) ⇒ Object
290
291
292
293
294
295
296
297
298
299
300
301
|
# File 'lib/fatty/session/popup_session.rb', line 290
def toggle_selected_item!(item)
label = item_label(item)
if selected_label?(label)
@selected_labels.delete(label)
else
@selected_labels[label] = true
end
refresh_displayed_items
item
end
|
#total_count ⇒ Object
Count methods for display to user.
305
306
307
|
# File 'lib/fatty/session/popup_session.rb', line 305
def total_count
@items.length
end
|
#view(screen:, renderer:) ⇒ Object
79
80
81
82
83
84
|
# File 'lib/fatty/session/popup_session.rb', line 79
def view(screen:, renderer:)
Fatty.debug("PopupSession#view: object_id=#{object_id} win_nil=#{@win.nil?}", tag: :session)
return unless @win
renderer.(session: self)
end
|