oficina / meet Public

Clone
git clone https://amadan.net/oficina/meet

Plain git — no account needed to clone.

Download

Download this file

1import CoreGraphics
2
3/// How the full-window call's control bar fits the width it has (2026-10-03:
4/// Paul's 0.4.6 bar ran off the left of the window, mute and camera cut
5/// off). The bar is always whole and centred: first its spacing tightens,
6/// then the controls that matter least move into a ⋯ menu, one at a time —
7/// never mute, camera, chat or Leave.
8public struct CallBarFit: Equatable, Sendable {
9 /// A control the bar can hold. `essential` ones never move into ⋯.
10 public enum Control: String, CaseIterable, Sendable {
11 case mute, camera, share, devices, layout, link, chat, leave
12
13 public var essential: Bool { [.mute, .camera, .chat, .leave].contains(self) }
14
15 /// Its width on the bar: every button is 44 pt round; Leave is a
16 /// capsule 1.4 times as wide (OverlayButtonBody).
17 var width: CGFloat { self == .leave ? CallBarFit.button * 1.4 : CallBarFit.button }
18 }
19
20 public static let button: CGFloat = 44
21 /// The widest the bar grows, and its margin from the window's edges.
22 public static let maxWidth: CGFloat = 760
23 public static let margin: CGFloat = 16
24 /// The order the optional controls move into ⋯: the least used first.
25 public static let overflowOrder: [Control] = [.link, .layout, .devices, .share]
26
27 /// One step of tightening: the gap between buttons, the least room
28 /// between the left group and chat, and the pane's padding.
29 public struct Spacing: Equatable, Sendable {
30 public var between: CGFloat
31 public var gap: CGFloat
32 public var padding: CGFloat
33 }
34
35 public static let roomy = Spacing(between: 10, gap: 24, padding: 10)
36 public static let snug = Spacing(between: 6, gap: 12, padding: 8)
37 public static let tight = Spacing(between: 4, gap: 4, padding: 6)
38
39 public var spacing: Spacing
40 /// The controls in ⋯, in the bar's own order; empty when everything fits.
41 public var overflow: [Control]
42 /// The pane's width: as wide as it may be, never wider than it has.
43 public var width: CGFloat
44
45 /// The bar for a window `windowWidth` wide showing `controls` (Share
46 /// meeting link is only there for a member; Video layout only here).
47 public static func fit(windowWidth: CGFloat, controls: [Control]) -> CallBarFit {
48 let room = max(0, windowWidth - 2 * margin)
49 for spacing in [roomy, snug, tight] where needed(controls, spacing, more: false) <= room {
50 return CallBarFit(spacing: spacing, overflow: [], width: min(maxWidth, room))
51 }
52 var shown = controls
53 var moved: [Control] = []
54 for control in overflowOrder where shown.contains(control) {
55 shown.removeAll { $0 == control }
56 moved.append(control)
57 if needed(shown, tight, more: true) <= room { break }
58 }
59 let order = controls.filter { moved.contains($0) }
60 return CallBarFit(spacing: tight, overflow: order, width: min(maxWidth, room))
61 }
62
63 /// The least width `controls` take with `spacing`, with a ⋯ button if
64 /// `more`: the buttons, a gap between each (the spacer before chat is
65 /// one more child of the row), the spacer's own least, and the padding.
66 public static func needed(_ controls: [Control], _ spacing: Spacing, more: Bool) -> CGFloat {
67 let buttons = controls.reduce(0) { $0 + $1.width } + (more ? button : 0)
68 let children = controls.count + (more ? 1 : 0) + 1
69 return buttons + CGFloat(children - 1) * spacing.between + spacing.gap + 2 * spacing.padding
70 }
71
72 public func shows(_ control: Control) -> Bool { !overflow.contains(control) }
73}
74