@effindomv2/fui-as 0.1.25 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effindomv2/fui-as",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "private": false,
5
5
  "license": "AGPL-3.0-only OR LicenseRef-EffinDom-Commercial",
6
6
  "description": "EffinDom v2 AssemblyScript frontend framework SDK and browser harness",
@@ -86,7 +86,7 @@
86
86
  },
87
87
  "dependencies": {
88
88
  "@assemblyscript/loader": "^0.28.17",
89
- "@effindomv2/runtime": "0.1.9"
89
+ "@effindomv2/runtime": "0.1.10"
90
90
  },
91
91
  "devDependencies": {
92
92
  "@as-pect/assembly": "8.1.0",
package/src/Fui.ts CHANGED
@@ -159,6 +159,7 @@ export {
159
159
  DropdownFieldTemplate,
160
160
  DropdownFieldVisualState,
161
161
  DropdownItem,
162
+ DropdownColors,
162
163
  DropdownOptionRowMetrics,
163
164
  DropdownOptionRowPresenter,
164
165
  DropdownOptionRowTemplate,
@@ -168,6 +169,7 @@ export {
168
169
  MenuItem,
169
170
  NavLink,
170
171
  LabeledControlSizing,
172
+ LabeledControlColors,
171
173
  PressableIndicatorMetrics,
172
174
  ProgressBar,
173
175
  RadioIndicatorPresenter,
@@ -178,6 +180,7 @@ export {
178
180
  SelectionArea,
179
181
  Slider,
180
182
  SliderSizing,
183
+ SliderColors,
181
184
  SliderPresenter,
182
185
  SliderPresenterMetrics,
183
186
  SliderTemplate,
@@ -190,6 +193,7 @@ export {
190
193
  TextInputPresenter,
191
194
  TextInput,
192
195
  TextInputTemplate,
196
+ TextInputColors,
193
197
  TextInputVisualState,
194
198
  useControlTemplates,
195
199
  } from "./controls";
@@ -23,6 +23,7 @@ import { registerScrollHook } from "../core/ScrollHooks";
23
23
  import { FlexBox, Portal, ScrollBarVisibility, ScrollBox, ScrollView } from "../nodes";
24
24
  import { bind2 } from "../core/bind";
25
25
  import { DropdownSizing } from "./ControlSizing";
26
+ import { DropdownColors } from "./DropdownColors";
26
27
  import { getControlTemplates } from "./ControlTemplateSet";
27
28
  import {
28
29
  createDefaultDropdownChevronPresenter,
@@ -158,10 +159,10 @@ class DropdownOptionNode extends FlexBox {
158
159
  return this.presenter.metrics.height;
159
160
  }
160
161
 
161
- applyTheme(theme: Theme, highlighted: bool, selected: bool, enabled: bool): void {
162
+ applyTheme(theme: Theme, highlighted: bool, selected: bool, enabled: bool, colors: DropdownColors | null): void {
162
163
  this.semanticSelected(selected);
163
164
  this.semanticDisabled(!enabled);
164
- this.presenter.apply(theme, new DropdownOptionRowVisualState(highlighted, selected, enabled));
165
+ this.presenter.apply(theme, new DropdownOptionRowVisualState(highlighted, selected, enabled), colors);
165
166
  }
166
167
 
167
168
  _handlePointerEvent(eventType: PointerEventType, x: f32, y: f32, modifiers: u32 = 0): void {
@@ -193,6 +194,7 @@ export class Dropdown extends FlexBox implements GlobalKeyHandler {
193
194
  private chevronTemplateValue: DropdownChevronTemplate | null = null;
194
195
  private optionRowTemplateValue: DropdownOptionRowTemplate | null = null;
195
196
  private sizingValue: DropdownSizing | null = null;
197
+ private colorsValue: DropdownColors | null = null;
196
198
  private fieldPresenter: DropdownFieldPresenter;
197
199
  private chevronPresenter: DropdownChevronPresenter;
198
200
  private readonly popupRoot: Portal;
@@ -375,6 +377,12 @@ export class Dropdown extends FlexBox implements GlobalKeyHandler {
375
377
  return this;
376
378
  }
377
379
 
380
+ colors(colors: DropdownColors | null): this {
381
+ this.colorsValue = colors;
382
+ this.handleThemeChanged();
383
+ return this;
384
+ }
385
+
378
386
  fieldTemplate(template: DropdownFieldTemplate | null): this {
379
387
  this.close();
380
388
  this.fieldTemplateValue = template;
@@ -768,6 +776,7 @@ export class Dropdown extends FlexBox implements GlobalKeyHandler {
768
776
  index == this.highlightedIndexValue,
769
777
  index == this.selectedIndexValue,
770
778
  this.isEnabled,
779
+ this.colorsValue,
771
780
  );
772
781
  }
773
782
  }
@@ -904,6 +913,7 @@ export class Dropdown extends FlexBox implements GlobalKeyHandler {
904
913
  ? unchecked(this.itemsValue[this.selectedIndexValue]).label
905
914
  : "",
906
915
  ),
916
+ this.colorsValue,
907
917
  );
908
918
  this.chevronPresenter.apply(
909
919
  theme,
@@ -0,0 +1,61 @@
1
+ export class DropdownColors {
2
+ private backgroundSet: bool = false;
3
+ private backgroundValue: u32 = 0;
4
+
5
+ private textPrimarySet: bool = false;
6
+ private textPrimaryValue: u32 = 0;
7
+
8
+ private placeholderSet: bool = false;
9
+ private placeholderValue: u32 = 0;
10
+
11
+ private borderSet: bool = false;
12
+ private borderValue: u32 = 0;
13
+
14
+ private accentSet: bool = false;
15
+ private accentValue: u32 = 0;
16
+
17
+ background(color: u32): this {
18
+ this.backgroundValue = color;
19
+ this.backgroundSet = true;
20
+ return this;
21
+ }
22
+
23
+ textPrimary(color: u32): this {
24
+ this.textPrimaryValue = color;
25
+ this.textPrimarySet = true;
26
+ return this;
27
+ }
28
+
29
+ placeholder(color: u32): this {
30
+ this.placeholderValue = color;
31
+ this.placeholderSet = true;
32
+ return this;
33
+ }
34
+
35
+ border(color: u32): this {
36
+ this.borderValue = color;
37
+ this.borderSet = true;
38
+ return this;
39
+ }
40
+
41
+ accent(color: u32): this {
42
+ this.accentValue = color;
43
+ this.accentSet = true;
44
+ return this;
45
+ }
46
+
47
+ get hasBackground(): bool { return this.backgroundSet; }
48
+ get backgroundColor(): u32 { return this.backgroundValue; }
49
+
50
+ get hasTextPrimary(): bool { return this.textPrimarySet; }
51
+ get textPrimaryColor(): u32 { return this.textPrimaryValue; }
52
+
53
+ get hasPlaceholder(): bool { return this.placeholderSet; }
54
+ get placeholderColor(): u32 { return this.placeholderValue; }
55
+
56
+ get hasBorder(): bool { return this.borderSet; }
57
+ get borderColor(): u32 { return this.borderValue; }
58
+
59
+ get hasAccent(): bool { return this.accentSet; }
60
+ get accentColor(): u32 { return this.accentValue; }
61
+ }
@@ -0,0 +1,25 @@
1
+ export class LabeledControlColors {
2
+ private textPrimarySet: bool = false;
3
+ private textPrimaryValue: u32 = 0;
4
+
5
+ private textMutedSet: bool = false;
6
+ private textMutedValue: u32 = 0;
7
+
8
+ textPrimary(color: u32): this {
9
+ this.textPrimaryValue = color;
10
+ this.textPrimarySet = true;
11
+ return this;
12
+ }
13
+
14
+ textMuted(color: u32): this {
15
+ this.textMutedValue = color;
16
+ this.textMutedSet = true;
17
+ return this;
18
+ }
19
+
20
+ get hasTextPrimary(): bool { return this.textPrimarySet; }
21
+ get textPrimaryColor(): u32 { return this.textPrimaryValue; }
22
+
23
+ get hasTextMuted(): bool { return this.textMutedSet; }
24
+ get textMutedColor(): u32 { return this.textMutedValue; }
25
+ }
@@ -21,6 +21,7 @@ import { Node } from "../core/Node";
21
21
  import { FlexBox } from "../nodes";
22
22
  import { bind1 } from "../core/bind";
23
23
  import { SliderSizing } from "./ControlSizing";
24
+ import { SliderColors } from "./SliderColors";
24
25
  import { getControlTemplates } from "./ControlTemplateSet";
25
26
  import {
26
27
  createDefaultSliderPresenter,
@@ -79,6 +80,7 @@ export class Slider extends FlexBox implements DragGestureHost {
79
80
  private sliderPresenter: SliderPresenter = createSliderPresenter(null, null);
80
81
  private templateOverride: SliderTemplate | null = null;
81
82
  private sizingValue: SliderSizing | null = null;
83
+ private colorsValue: SliderColors | null = null;
82
84
  private readonly disposables: Array<Disposable> = new Array<Disposable>();
83
85
  private readonly dragGesture!: DragGesture;
84
86
  private changedCallback: ((value: f32) => void) | null = null;
@@ -190,6 +192,12 @@ export class Slider extends FlexBox implements DragGestureHost {
190
192
  return this;
191
193
  }
192
194
 
195
+ colors(colors: SliderColors | null): this {
196
+ this.colorsValue = colors;
197
+ this.syncPresentation();
198
+ return this;
199
+ }
200
+
193
201
  template(template: SliderTemplate | null): this {
194
202
  this.templateOverride = template;
195
203
  const nextPresenter = createSliderPresenter(this.templateOverride, this.sizingValue);
@@ -398,7 +406,7 @@ export class Slider extends FlexBox implements DragGestureHost {
398
406
  );
399
407
  this.padding(SLIDER_PADDING, SLIDER_PADDING, SLIDER_PADDING, SLIDER_PADDING);
400
408
  this.opacity(this.isEnabled ? 1.0 : 0.6);
401
- this.sliderPresenter.apply(theme, state);
409
+ this.sliderPresenter.apply(theme, state, this.colorsValue);
402
410
  }
403
411
 
404
412
  private syncVisualState(): void {
@@ -0,0 +1,37 @@
1
+ export class SliderColors {
2
+ private trackSet: bool = false;
3
+ private trackValue: u32 = 0;
4
+
5
+ private fillSet: bool = false;
6
+ private fillValue: u32 = 0;
7
+
8
+ private thumbSet: bool = false;
9
+ private thumbValue: u32 = 0;
10
+
11
+ track(color: u32): this {
12
+ this.trackValue = color;
13
+ this.trackSet = true;
14
+ return this;
15
+ }
16
+
17
+ fill(color: u32): this {
18
+ this.fillValue = color;
19
+ this.fillSet = true;
20
+ return this;
21
+ }
22
+
23
+ thumb(color: u32): this {
24
+ this.thumbValue = color;
25
+ this.thumbSet = true;
26
+ return this;
27
+ }
28
+
29
+ get hasTrack(): bool { return this.trackSet; }
30
+ get trackColor(): u32 { return this.trackValue; }
31
+
32
+ get hasFill(): bool { return this.fillSet; }
33
+ get fillColor(): u32 { return this.fillValue; }
34
+
35
+ get hasThumb(): bool { return this.thumbSet; }
36
+ get thumbColor(): u32 { return this.thumbValue; }
37
+ }
@@ -0,0 +1,85 @@
1
+ export class TextInputColors {
2
+ private backgroundSet: bool = false;
3
+ private backgroundValue: u32 = 0;
4
+
5
+ private textPrimarySet: bool = false;
6
+ private textPrimaryValue: u32 = 0;
7
+
8
+ private textMutedSet: bool = false;
9
+ private textMutedValue: u32 = 0;
10
+
11
+ private placeholderSet: bool = false;
12
+ private placeholderValue: u32 = 0;
13
+
14
+ private caretSet: bool = false;
15
+ private caretValue: u32 = 0;
16
+
17
+ private borderSet: bool = false;
18
+ private borderValue: u32 = 0;
19
+
20
+ private accentSet: bool = false;
21
+ private accentValue: u32 = 0;
22
+
23
+ background(color: u32): this {
24
+ this.backgroundValue = color;
25
+ this.backgroundSet = true;
26
+ return this;
27
+ }
28
+
29
+ textPrimary(color: u32): this {
30
+ this.textPrimaryValue = color;
31
+ this.textPrimarySet = true;
32
+ return this;
33
+ }
34
+
35
+ textMuted(color: u32): this {
36
+ this.textMutedValue = color;
37
+ this.textMutedSet = true;
38
+ return this;
39
+ }
40
+
41
+ placeholder(color: u32): this {
42
+ this.placeholderValue = color;
43
+ this.placeholderSet = true;
44
+ return this;
45
+ }
46
+
47
+ caret(color: u32): this {
48
+ this.caretValue = color;
49
+ this.caretSet = true;
50
+ return this;
51
+ }
52
+
53
+ border(color: u32): this {
54
+ this.borderValue = color;
55
+ this.borderSet = true;
56
+ return this;
57
+ }
58
+
59
+ accent(color: u32): this {
60
+ this.accentValue = color;
61
+ this.accentSet = true;
62
+ return this;
63
+ }
64
+
65
+ get hasBackground(): bool { return this.backgroundSet; }
66
+ get backgroundColor(): u32 { return this.backgroundValue; }
67
+
68
+ get hasTextPrimary(): bool { return this.textPrimarySet; }
69
+ get textPrimaryColor(): u32 { return this.textPrimaryValue; }
70
+
71
+ get hasTextMuted(): bool { return this.textMutedSet; }
72
+ get textMutedColor(): u32 { return this.textMutedValue; }
73
+
74
+ get hasPlaceholder(): bool { return this.placeholderSet; }
75
+ get placeholderColor(): u32 { return this.placeholderValue; }
76
+
77
+ get hasCaret(): bool { return this.caretSet; }
78
+ get caretColor(): u32 { return this.caretValue; }
79
+
80
+ get hasBorder(): bool { return this.borderSet; }
81
+ get borderColor(): u32 { return this.borderValue; }
82
+
83
+ get hasAccent(): bool { return this.accentSet; }
84
+ get accentColor(): u32 { return this.accentValue; }
85
+ }
@@ -1,6 +1,10 @@
1
1
  export { Button } from "./Button";
2
2
  export { Checkbox } from "./Checkbox";
3
3
  export { DropdownSizing, LabeledControlSizing, SliderSizing } from "./ControlSizing";
4
+ export { LabeledControlColors } from "./LabeledControlColors";
5
+ export { TextInputColors } from "./TextInputColors";
6
+ export { DropdownColors } from "./DropdownColors";
7
+ export { SliderColors } from "./SliderColors";
4
8
  export { ContextMenu, MenuItem } from "./ContextMenu";
5
9
  export { Dialog } from "./Dialog";
6
10
  export { Dropdown, DropdownItem } from "./Dropdown";
@@ -9,6 +9,7 @@ import {
9
9
  import { Theme } from "../../core/Theme";
10
10
  import { FlexBox, Text } from "../../nodes";
11
11
  import { DropdownSizing } from "../ControlSizing";
12
+ import { DropdownColors } from "../DropdownColors";
12
13
 
13
14
  const DEFAULT_CHEVRON_BOX_SIZE: f32 = 16.0;
14
15
  const DEFAULT_FIELD_PADDING_X: f32 = 16.0;
@@ -98,7 +99,7 @@ export abstract class DropdownFieldPresenter {
98
99
  return this.metricsValue;
99
100
  }
100
101
 
101
- abstract apply(theme: Theme, state: DropdownFieldVisualState): void;
102
+ abstract apply(theme: Theme, state: DropdownFieldVisualState, colors?: DropdownColors | null): void;
102
103
  }
103
104
 
104
105
  export abstract class DropdownFieldTemplate {
@@ -131,26 +132,31 @@ class DefaultDropdownFieldPresenter extends DropdownFieldPresenter {
131
132
  super(root, valueHost, valueNode, chevronHost, metrics);
132
133
  }
133
134
 
134
- apply(theme: Theme, state: DropdownFieldVisualState): void {
135
+ apply(theme: Theme, state: DropdownFieldVisualState, colors: DropdownColors | null = null): void {
135
136
  const metrics = this.metrics;
136
137
  const contentHeight = <f32>Math.max(
137
138
  metrics.fontSize,
138
139
  metrics.height - metrics.paddingTop - metrics.paddingBottom,
139
140
  );
141
+ const bg = colors !== null && colors.hasBackground ? colors.backgroundColor : (state.pressed && state.enabled ? theme.colors.background : theme.colors.surface);
142
+ const borderColor = colors !== null && colors.hasBorder ? colors.borderColor : theme.colors.border;
140
143
  this.root
141
144
  .flexDirection(FlexDirection.Row)
142
145
  .alignItems(AlignItems.Center)
143
146
  .height(metrics.height, Unit.Pixel)
144
147
  .cornerRadius(theme.spacing.sm)
145
- .border(2.0, theme.colors.border, BorderStyle.Solid)
148
+ .border(2.0, borderColor, BorderStyle.Solid)
146
149
  .padding(metrics.paddingLeft, metrics.paddingTop, metrics.paddingRight, metrics.paddingBottom)
147
- .bgColor(state.pressed && state.enabled ? theme.colors.background : theme.colors.surface);
150
+ .bgColor(bg);
148
151
  this.valueHost
149
152
  .fillSize();
153
+ const textColor = !state.enabled
154
+ ? theme.colors.textMuted
155
+ : (colors !== null && colors.hasTextPrimary ? colors.textPrimaryColor : theme.colors.textPrimary);
150
156
  this.valueNode
151
157
  .font(theme.fonts.body, metrics.fontSize)
152
158
  .lineHeight(contentHeight)
153
- .textColor(state.enabled ? theme.colors.textPrimary : theme.colors.textMuted);
159
+ .textColor(textColor);
154
160
  this.chevronHost
155
161
  .width(metrics.chevronBoxSize, Unit.Pixel)
156
162
  .height(metrics.chevronBoxSize, Unit.Pixel)
@@ -2,6 +2,7 @@ import { AlignItems, TextVerticalAlign } from "../../core/ffi";
2
2
  import { Theme } from "../../core/Theme";
3
3
  import { FlexBox, Text } from "../../nodes";
4
4
  import { DropdownSizing } from "../ControlSizing";
5
+ import { DropdownColors } from "../DropdownColors";
5
6
 
6
7
  export class DropdownOptionRowMetrics {
7
8
  constructor(
@@ -55,7 +56,7 @@ export abstract class DropdownOptionRowPresenter {
55
56
  return this.metricsValue;
56
57
  }
57
58
 
58
- abstract apply(theme: Theme, state: DropdownOptionRowVisualState): void;
59
+ abstract apply(theme: Theme, state: DropdownOptionRowVisualState, colors?: DropdownColors | null): void;
59
60
  }
60
61
 
61
62
  export abstract class DropdownOptionRowTemplate {
@@ -79,19 +80,20 @@ class DefaultDropdownOptionRowPresenter extends DropdownOptionRowPresenter {
79
80
  super(root, labelNode, metrics);
80
81
  }
81
82
 
82
- apply(theme: Theme, state: DropdownOptionRowVisualState): void {
83
+ apply(theme: Theme, state: DropdownOptionRowVisualState, colors: DropdownColors | null = null): void {
83
84
  const metrics = this.metrics;
84
85
  this.root
85
86
  .padding(metrics.paddingLeft, 0.0, metrics.paddingRight, 0.0)
86
87
  .cornerRadius(theme.spacing.xs)
87
88
  .bgColor(state.highlighted ? theme.contextMenu.item.hoverBackground : 0x00000000);
89
+ const labelColor = !state.enabled
90
+ ? theme.colors.textMuted
91
+ : (state.selected
92
+ ? (colors !== null && colors.hasAccent ? colors.accentColor : theme.colors.accent)
93
+ : (colors !== null && colors.hasTextPrimary ? colors.textPrimaryColor : theme.colors.textPrimary));
88
94
  this.labelNode
89
95
  .font(theme.fonts.body, metrics.fontSize)
90
- .textColor(
91
- !state.enabled
92
- ? theme.colors.textMuted
93
- : (state.selected ? theme.colors.accent : theme.colors.textPrimary),
94
- );
96
+ .textColor(labelColor);
95
97
  }
96
98
  }
97
99
 
@@ -16,6 +16,7 @@ import {
16
16
  } from "../../core/ffi";
17
17
  import { Theme, activeTheme } from "../../core/Theme";
18
18
  import { FlexBox, TextCore } from "../../nodes";
19
+ import { LabeledControlColors } from "../LabeledControlColors";
19
20
 
20
21
  const TRANSPARENT: u32 = rgba(0x00, 0x00, 0x00, 0x00);
21
22
 
@@ -31,6 +32,7 @@ export class PressableLabeledControl extends FlexBox {
31
32
  private readonly disposables: Array<Disposable> = new Array<Disposable>();
32
33
  private disposed: bool = false;
33
34
  private labelFontSizeOverride: f32 = 0.0;
35
+ private colorsValue: LabeledControlColors | null = null;
34
36
  protected hoveredState: bool = false;
35
37
  protected pressedState: bool = false;
36
38
  protected focusedState: bool = false;
@@ -177,6 +179,12 @@ export class PressableLabeledControl extends FlexBox {
177
179
  this.syncBaseTheme(activeTheme.value);
178
180
  }
179
181
 
182
+ colors(colors: LabeledControlColors | null): this {
183
+ this.colorsValue = colors;
184
+ this.syncBaseTheme(activeTheme.value);
185
+ return this;
186
+ }
187
+
180
188
  protected syncBaseTheme(theme: Theme): void {
181
189
  this.cursor(this.isEnabled ? CursorStyle.Pointer : CursorStyle.Default);
182
190
  this.cornerRadius(theme.spacing.sm);
@@ -192,7 +200,22 @@ export class PressableLabeledControl extends FlexBox {
192
200
  theme.fonts.body,
193
201
  this.labelFontSizeOverride > 0.0 ? this.labelFontSizeOverride : theme.fonts.sizeBody,
194
202
  );
195
- this.labelNode.textColor(this.isEnabled ? theme.colors.textPrimary : theme.colors.textMuted);
203
+ const colors = this.colorsValue;
204
+ let labelColor: u32;
205
+ if (this.isEnabled) {
206
+ if (colors !== null && colors.hasTextPrimary) {
207
+ labelColor = colors.textPrimaryColor;
208
+ } else {
209
+ labelColor = theme.colors.textPrimary;
210
+ }
211
+ } else {
212
+ if (colors !== null && colors.hasTextMuted) {
213
+ labelColor = colors.textMutedColor;
214
+ } else {
215
+ labelColor = theme.colors.textMuted;
216
+ }
217
+ }
218
+ this.labelNode.textColor(labelColor);
196
219
  this.syncFocusChrome(theme);
197
220
  }
198
221
 
@@ -2,6 +2,7 @@ import { BorderStyle, Orientation, Unit } from "../../core/ffi";
2
2
  import { Theme } from "../../core/Theme";
3
3
  import { FlexBox } from "../../nodes";
4
4
  import { SliderSizing } from "../ControlSizing";
5
+ import { SliderColors } from "../SliderColors";
5
6
 
6
7
  function clamp(value: f32, min: f32, max: f32): f32 {
7
8
  if (value < min) {
@@ -76,7 +77,7 @@ export abstract class SliderPresenter {
76
77
  }
77
78
 
78
79
  abstract layout(state: SliderVisualState, length: f32): void;
79
- abstract apply(theme: Theme, state: SliderVisualState): void;
80
+ abstract apply(theme: Theme, state: SliderVisualState, colors?: SliderColors | null): void;
80
81
  }
81
82
 
82
83
  export abstract class SliderTemplate {
@@ -163,7 +164,7 @@ class DefaultSliderPresenter extends SliderPresenter {
163
164
  );
164
165
  }
165
166
 
166
- apply(theme: Theme, state: SliderVisualState): void {
167
+ apply(theme: Theme, state: SliderVisualState, colors: SliderColors | null = null): void {
167
168
  const accent = state.dragging
168
169
  ? theme.colors.accentPressed
169
170
  : (state.hovered ? theme.colors.accentHovered : theme.colors.accent);
@@ -172,14 +173,17 @@ class DefaultSliderPresenter extends SliderPresenter {
172
173
  const trackThickness = resolveTrackThickness(metrics);
173
174
  const trackRadius = trackThickness * 0.5;
174
175
  this.trackNode.cornerRadius(trackRadius);
175
- this.trackNode.bgColor(theme.colors.scrollbarTrack);
176
+ const trackColor = colors !== null && colors.hasTrack ? colors.trackColor : theme.colors.scrollbarTrack;
177
+ this.trackNode.bgColor(trackColor);
176
178
  this.fillNode.cornerRadius(trackRadius);
177
- this.fillNode.bgColor(accent);
179
+ const fillColor = colors !== null && colors.hasFill ? colors.fillColor : accent;
180
+ this.fillNode.bgColor(fillColor);
178
181
  this.thumbNode
179
182
  .width(thumbSize, Unit.Pixel)
180
183
  .height(thumbSize, Unit.Pixel)
181
184
  .cornerRadius(thumbSize * 0.5);
182
- this.thumbNode.bgColor(accent);
185
+ const thumbColor = colors !== null && colors.hasThumb ? colors.thumbColor : accent;
186
+ this.thumbNode.bgColor(thumbColor);
183
187
  this.thumbNode.border(1.0, theme.colors.surface, BorderStyle.Solid);
184
188
  }
185
189
  }
@@ -22,6 +22,7 @@ import { bind1, bind2 } from "../../core/bind";
22
22
  import { ScrollState } from "../../nodes/ScrollState";
23
23
  import { ScrollView } from "../../nodes/ScrollView";
24
24
  import { getControlTemplates } from "../ControlTemplateSet";
25
+ import { TextInputColors } from "../TextInputColors";
25
26
  import {
26
27
  defaultTextInputTemplate,
27
28
  TextInputPresenter,
@@ -179,6 +180,7 @@ export class TextInputCore extends FlexBox {
179
180
  private focusChangedListener: ((focused: bool) => void) | null = null;
180
181
  private textInputFocusChangedBinding: Callback1<bool> | null = null;
181
182
  private templateValue: TextInputTemplate | null = null;
183
+ private colorsValue: TextInputColors | null = null;
182
184
 
183
185
  constructor(profile: TextInputProfile, text: string = "") {
184
186
  super();
@@ -256,6 +258,12 @@ export class TextInputCore extends FlexBox {
256
258
  return this;
257
259
  }
258
260
 
261
+ colors(colors: TextInputColors | null): this {
262
+ this.colorsValue = colors;
263
+ this.syncThemeState(activeTheme.value);
264
+ return this;
265
+ }
266
+
259
267
  text(value: string): this {
260
268
  this.textValue = value;
261
269
  this.clampSelectionToText();
@@ -461,30 +469,49 @@ export class TextInputCore extends FlexBox {
461
469
  const resolvedFontSize = this.hasFontSizeOverride ? this.fontSizeOverride : theme.fonts.sizeBody;
462
470
  const lineHeight = resolvedFontSize + theme.spacing.sm;
463
471
  const editableCursor = this.isEnabled ? CursorStyle.Text : CursorStyle.Default;
464
- this.presenter.apply(theme, this.createVisualState());
472
+ this.presenter.apply(theme, this.createVisualState(), this.colorsValue);
465
473
 
466
474
  const textVerticalAlign = this.profile.multiline ? TextVerticalAlign.Top : TextVerticalAlign.Center;
475
+
476
+ const colors = this.colorsValue;
477
+ let resolvedTextColor: u32;
478
+ if (this.isEnabled) {
479
+ if (colors !== null && colors.hasTextPrimary) {
480
+ resolvedTextColor = colors.textPrimaryColor;
481
+ } else {
482
+ resolvedTextColor = theme.colors.textPrimary;
483
+ }
484
+ } else {
485
+ if (colors !== null && colors.hasTextMuted) {
486
+ resolvedTextColor = colors.textMutedColor;
487
+ } else {
488
+ resolvedTextColor = theme.colors.textMuted;
489
+ }
490
+ }
491
+ const resolvedCaretColor = colors !== null && colors.hasCaret ? colors.caretColor : theme.colors.accent;
492
+
467
493
  this.editorText
468
494
  .width(this.shouldEditorTrackViewportWidth() ? 100.0 : 0.0, this.shouldEditorTrackViewportWidth() ? Unit.Percent : Unit.Auto)
469
495
  .height(this.profile.multiline ? 0.0 : lineHeight, this.profile.multiline ? Unit.Auto : Unit.Pixel)
470
496
  .fontFamily(resolvedFontFamily)
471
497
  .fontSize(resolvedFontSize)
472
498
  .verticalAlign(textVerticalAlign)
473
- .textColor(this.isEnabled ? theme.colors.textPrimary : theme.colors.textMuted)
474
- .caretColor(theme.colors.accent);
499
+ .textColor(resolvedTextColor)
500
+ .caretColor(resolvedCaretColor);
475
501
  this.syncEditorWrapping();
476
502
 
477
503
  this.placeholderHost
478
504
  .width(100.0, Unit.Percent)
479
505
  .height(this.profile.multiline ? 0.0 : lineHeight, this.profile.multiline ? Unit.Auto : Unit.Pixel)
480
506
  .cursor(editableCursor);
507
+ const resolvedPlaceholderColor = colors !== null && colors.hasPlaceholder ? colors.placeholderColor : theme.colors.textMuted;
481
508
  this.placeholderText
482
509
  .width(100.0, Unit.Percent)
483
510
  .height(this.profile.multiline ? 0.0 : lineHeight, this.profile.multiline ? Unit.Auto : Unit.Pixel)
484
511
  .fontFamily(resolvedFontFamily)
485
512
  .fontSize(resolvedFontSize)
486
513
  .verticalAlign(textVerticalAlign)
487
- .textColor(theme.colors.textMuted);
514
+ .textColor(resolvedPlaceholderColor);
488
515
  const scrollBox = this.editorScrollBox;
489
516
  if (scrollBox !== null) {
490
517
  scrollBox.cursor(CursorStyle.Default);
@@ -492,6 +519,14 @@ export class TextInputCore extends FlexBox {
492
519
  scrollBox.fillSize();
493
520
  this.syncScrollChromeState();
494
521
  }
522
+
523
+ if (colors !== null && colors.hasBackground) {
524
+ this.bgColor(colors.backgroundColor);
525
+ }
526
+ if (colors !== null && colors.hasBorder) {
527
+ this.border(1.0, colors.borderColor, BorderStyle.Solid);
528
+ }
529
+
495
530
  this.syncFocusChrome(theme);
496
531
  }
497
532
 
@@ -2,6 +2,7 @@ import { BorderStyle, CursorStyle, Unit } from "../../core/ffi";
2
2
  import { Node } from "../../core/Node";
3
3
  import { Theme } from "../../core/Theme";
4
4
  import { FlexBox } from "../../nodes";
5
+ import { TextInputColors } from "../TextInputColors";
5
6
 
6
7
  export class TextInputVisualState {
7
8
  constructor(
@@ -35,7 +36,7 @@ export abstract class TextInputPresenter {
35
36
  return changetype<FlexBox>(this.placeholderHostValue);
36
37
  }
37
38
 
38
- abstract apply(theme: Theme, state: TextInputVisualState): void;
39
+ abstract apply(theme: Theme, state: TextInputVisualState, colors?: TextInputColors | null): void;
39
40
  }
40
41
 
41
42
  export abstract class TextInputTemplate {
@@ -43,15 +44,17 @@ export abstract class TextInputTemplate {
43
44
  }
44
45
 
45
46
  class DefaultTextInputPresenter extends TextInputPresenter {
46
- apply(theme: Theme, state: TextInputVisualState): void {
47
+ apply(theme: Theme, state: TextInputVisualState, colors: TextInputColors | null = null): void {
47
48
  const horizontalPadding = theme.spacing.md;
48
49
  const verticalPadding = theme.spacing.sm;
49
50
  const editableCursor = state.enabled ? CursorStyle.Text : CursorStyle.Default;
50
51
  const shellCursor = !state.multiline && state.enabled ? CursorStyle.Text : CursorStyle.Default;
52
+ const bg = colors !== null && colors.hasBackground ? colors.backgroundColor : theme.colors.surface;
53
+ const borderColor = colors !== null && colors.hasBorder ? colors.borderColor : theme.colors.border;
51
54
  this.host
52
- .bgColor(theme.colors.surface)
55
+ .bgColor(bg)
53
56
  .cornerRadius(theme.spacing.sm)
54
- .border(1.0, theme.colors.border, BorderStyle.Solid)
57
+ .border(1.0, borderColor, BorderStyle.Solid)
55
58
  .padding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
56
59
  .cursor(shellCursor);
57
60
  this.host.opacity(state.enabled ? 1.0 : 0.6);