Module: Plushie::Runtime::Windows

Defined in:
lib/plushie/runtime/windows.rb

Overview

Window lifecycle management for the Plushie runtime.

Detects window nodes in the UI tree, opens/closes/updates windows via the bridge, and tracks the set of active window IDs.

Constant Summary collapse

WINDOW_PROP_KEYS =

Window setting keys that can be specified as node props on window elements.

%i[
  title size width height position min_size max_size maximized fullscreen
  visible resizable closeable minimizable decorations transparent blur level
  exit_on_close_request scale_factor theme
].freeze

Class Method Summary collapse

Class Method Details

.apply_ops(runtime, ops, tracked_windows) ⇒ Array(Set<String>, bool)

Apply planned window operations, updating tracked windows only after each operation is accepted by the bridge.

whether any operation was accepted by the bridge

Parameters:

  • runtime (Runtime)

    the runtime instance

  • ops (Array<Hash>)

    planned operations from plan_sync

  • tracked_windows (Set<String>)

    currently tracked window IDs

Returns:

  • (Array(Set<String>, bool))

    updated tracked window IDs and



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
# File 'lib/plushie/runtime/windows.rb', line 86

def self.apply_ops(runtime, ops, tracked_windows)
  accepted = false
  ops.each do |op|
    op_name = op.fetch(:op)
    window_id = op.fetch(:window_id)
    settings = op.fetch(:settings)
    if op_name == "open"
      base_settings = begin
        runtime.app.window_config(runtime.model)
      rescue => e
        runtime.logger.warn("plushie: window_config error: #{e.class}: #{e.message}")
        {}
      end
      settings = base_settings.merge(settings)
    end

    runtime.bridge_send_window_op(op_name, window_id, settings)
    accepted = true

    case op_name
    when "open"
      tracked_windows.add(window_id)
    when "close"
      tracked_windows.delete(window_id)
    end
  end

  [tracked_windows, accepted]
end

.collect_window_ids(node, ids) ⇒ Object



134
135
136
137
138
139
# File 'lib/plushie/runtime/windows.rb', line 134

def self.collect_window_ids(node, ids)
  ids << node.id if node.type == "window"
  return unless node.respond_to?(:children) && node.children

  node.children.each { |child| collect_window_ids(child, ids) }
end

.decompose_nested_size(props, key) ⇒ Object



170
171
172
173
174
175
# File 'lib/plushie/runtime/windows.rb', line 170

def self.decompose_nested_size(props, key)
  val = props[key]
  return props unless val.is_a?(Array) && val.length == 2

  props.merge(key => {width: val[0], height: val[1]})
end

.decompose_size(props) ⇒ Object



160
161
162
163
164
165
166
167
168
# File 'lib/plushie/runtime/windows.rb', line 160

def self.decompose_size(props)
  size = props[:size]
  return props unless size.is_a?(Array) && size.length == 2

  props = props.except(:size)
  props[:width] ||= size[0]
  props[:height] ||= size[1]
  props
end

.decompose_size_tuples(props) ⇒ Object



154
155
156
157
158
# File 'lib/plushie/runtime/windows.rb', line 154

def self.decompose_size_tuples(props)
  props = decompose_size(props)
  props = decompose_nested_size(props, :min_size)
  decompose_nested_size(props, :max_size)
end

.detect_windows(tree) ⇒ Set<String>

Detect window node IDs from the tree.

Parameters:

Returns:

  • (Set<String>)


21
22
23
24
25
26
27
# File 'lib/plushie/runtime/windows.rb', line 21

def self.detect_windows(tree)
  return Set.new unless tree

  ids = []
  collect_window_ids(tree, ids)
  Set.new(ids)
end

.extract_window_props(tree, window_id) ⇒ Hash

Extract window-related props from a window node in the tree.

Parameters:

  • tree (Node, nil)
  • window_id (String)

Returns:

  • (Hash)


121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/plushie/runtime/windows.rb', line 121

def self.extract_window_props(tree, window_id)
  return {} unless tree

  node = find_window_node(tree, window_id)
  return {} unless node

  props = node.props
    .select { |k, _| WINDOW_PROP_KEYS.include?(k.to_sym) }
    .to_h { |k, v| [k.to_sym, v] }

  decompose_size_tuples(props)
end

.find_window_node(node, window_id) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/plushie/runtime/windows.rb', line 141

def self.find_window_node(node, window_id)
  return node if node.type == "window" && node.id == window_id

  if node.respond_to?(:children) && node.children
    node.children.each do |child|
      found = find_window_node(child, window_id)
      return found if found
    end
  end

  nil
end

.plan_sync(runtime, new_tree, previous_tree, tracked_windows) ⇒ Array(Set<String>, Array<Hash>)

Plan window synchronization without sending anything to the renderer.

Parameters:

  • runtime (Runtime)

    the runtime instance

  • new_tree (Node, nil)

    the newly rendered tree

  • previous_tree (Node, nil)

    the previous tree (for prop diffing)

  • tracked_windows (Set<String>)

    currently tracked window IDs

Returns:

  • (Array(Set<String>, Array<Hash>))

    detected windows and ordered ops



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/plushie/runtime/windows.rb', line 53

def self.plan_sync(runtime, new_tree, previous_tree, tracked_windows)
  new_windows = detect_windows(new_tree)
  opened = new_windows - tracked_windows
  closed = tracked_windows - new_windows
  surviving = tracked_windows & new_windows
  ops = []

  opened.each do |window_id|
    per_window_props = extract_window_props(new_tree, window_id)
    ops << {op: "open", window_id:, settings: per_window_props}
  end

  closed.each do |window_id|
    ops << {op: "close", window_id:, settings: {}}
  end

  surviving.each do |window_id|
    old_props = extract_window_props(previous_tree, window_id)
    new_props = extract_window_props(new_tree, window_id)
    ops << {op: "update", window_id:, settings: new_props} if old_props != new_props
  end

  [new_windows, ops]
end

.sync_windows(runtime, new_tree, previous_tree, tracked_windows) ⇒ Set<String>

Synchronize tracked windows with the current tree.

Opens new windows (calling window_config for base settings), closes removed windows, and sends update ops for windows whose props changed.

Parameters:

  • runtime (Runtime)

    the runtime instance

  • new_tree (Node, nil)

    the newly rendered tree

  • previous_tree (Node, nil)

    the previous tree (for prop diffing)

  • tracked_windows (Set<String>)

    currently tracked window IDs

Returns:

  • (Set<String>)

    the new set of tracked window IDs



40
41
42
43
44
# File 'lib/plushie/runtime/windows.rb', line 40

def self.sync_windows(runtime, new_tree, previous_tree, tracked_windows)
  _new_windows, ops = plan_sync(runtime, new_tree, previous_tree, tracked_windows)
  next_tracked_windows, _accepted = apply_ops(runtime, ops, tracked_windows)
  next_tracked_windows
end