@onekeyfe/react-native-native-list 6.7.0-alpha.411 → 6.7.0-alpha.412
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/android/src/main/java/com/onekey/nativelist/NativeListAccessoryStack.kt +5 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListGeneratedIcons.kt +47 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +5 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +117 -338
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +5 -0
- package/docs/SPEC.md +114 -7
- package/ios/NativeListAccessoryStack.swift +6 -16
- package/ios/NativeListDataRowRenderer.swift +15 -6
- package/ios/NativeListDesignAssets.swift +13 -84
- package/ios/NativeListGeneratedIcons.swift +55 -0
- package/ios/NativeListIconBinding.swift +171 -0
- package/ios/NativeListIdentityRenderer.swift +12 -10
- package/ios/NativeListLeadingVisual.swift +11 -9
- package/ios/NativeListMarketRenderer.swift +7 -13
- package/ios/NativeListMediaTileRenderer.swift +6 -2
- package/ios/NativeListMetricCardRenderer.swift +4 -2
- package/ios/NativeListSectionHeaderRenderer.swift +12 -4
- package/ios/tests/NativeListAccessoryStackTests.swift +12 -0
- package/ios/tests/NativeListSharedIconTests.swift +236 -0
- package/lib/module/web/NativeListGeneratedIcons.js +31 -0
- package/lib/module/web/NativeListWebEngine.js +21 -323
- package/lib/module/web/templates/ActivityRowRenderer.js +4 -24
- package/lib/module/web/templates/MarketTableRowRenderer.js +11 -15
- package/lib/module/web/templates/MediaTileRowRenderer.js +2 -12
- package/lib/typescript/src/web/NativeListGeneratedIcons.d.ts +2 -0
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +0 -1
- package/package.json +10 -4
- package/react-native-native-list.podspec +0 -5
- package/scripts/generate-icons.mjs +150 -0
- package/scripts/generate-icons.test.mjs +288 -0
- package/scripts/native-list-icons.json +260 -0
- package/src/web/NativeListGeneratedIcons.ts +28 -0
- package/src/web/NativeListWebEngine.ts +25 -405
- package/src/web/templates/ActivityRowRenderer.ts +15 -53
- package/src/web/templates/MarketTableRowRenderer.ts +17 -29
- package/src/web/templates/MediaTileRowRenderer.ts +2 -15
- package/ios/Resources/NativeListIcons.xcassets/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_bottom.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_bottom.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_top.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_arrow_top.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_badge_verified_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_badge_verified_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chart_trending_up.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chart_trending_up.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_checked.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_checked.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_indeterminate.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_checkbox_indeterminate.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_bottom.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_bottom.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_grabber_ver.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_grabber_ver.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_right_small.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_right_small.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_top.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_chevron_top.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/icon.svg +0 -4
- package/ios/Resources/NativeListIcons.xcassets/onekey_document2.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_document2.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_hor.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_hor.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_ver_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_dot_ver_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_drag.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_drag.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_error_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_error_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/icon.svg +0 -3
- package/ios/Resources/NativeListIcons.xcassets/onekey_image_square_waves.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_image_square_waves.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_info_circle.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_info_circle.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_minus_circle_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_pencil.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_pencil.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_circle.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_circle.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_large.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_large.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_small.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_plus_small.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_questionmark_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_questionmark_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/icon.svg +0 -3
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_account_error_custom.imageset/icon.svg +0 -14
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_all_networks_solid.imageset/icon.svg +0 -12
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_apple_brand.imageset/icon.svg +0 -6
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_bot_illus.imageset/icon.svg +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_crossed_small_solid.imageset/icon.svg +0 -3
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_globus_outline.imageset/icon.svg +0 -7
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_google_illus.imageset/icon.svg +0 -18
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/Contents.json +0 -15
- package/ios/Resources/NativeListIcons.xcassets/onekey_selector_lock_solid.imageset/icon.svg +0 -7
- package/ios/Resources/NativeListIcons.xcassets/onekey_shield_exclamation.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_shield_exclamation.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_speaker_promote.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_speaker_promote.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star_solid.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_star_solid.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_swap_hor.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_swap_hor.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_ticket.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_ticket.imageset/icon.svg +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_wallet.imageset/Contents.json +0 -1
- package/ios/Resources/NativeListIcons.xcassets/onekey_wallet.imageset/icon.svg +0 -1
package/docs/SPEC.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# NativeList contract
|
|
2
2
|
|
|
3
|
+
## Shared image SVG integration — Implemented
|
|
4
|
+
|
|
5
|
+
Static icons use the image package's named resources and SVG implementation:
|
|
6
|
+
the main registry, Activity's ten private glyphs, Market stars/recognized badge,
|
|
7
|
+
Media fallback and Android checkbox. The closed per-platform coverage, including
|
|
8
|
+
the main-line asset-list additions, is recorded under Built-in icon metadata
|
|
9
|
+
generation; unknown-name text fallbacks remain. The NativeList-only legacy name
|
|
10
|
+
`BadgeVerifiedSolid` resolves to canonical `BadgeRecognizedSolid`. Circle and
|
|
11
|
+
dynamic charts/spinners/dotted decoration remain local primitives.
|
|
12
|
+
|
|
13
|
+
Each iOS glyph slot owns a request owner and revision, including overlays and
|
|
14
|
+
UIButton states. Final graphical identity (name, raster geometry/scale and render
|
|
15
|
+
strategy) controls requests. A private synchronous bind transaction preserves
|
|
16
|
+
both an identical pending request and a ready raster across reset/rebind calls;
|
|
17
|
+
changed or empty identities cancel and clear. Only the current attached revision
|
|
18
|
+
may apply a completion. Detach, reuse and raster geometry changes invalidate
|
|
19
|
+
ownership. UIKit template tint changes reuse the immutable mask. UIButton states
|
|
20
|
+
share one decode, retaining template/original and disabled tint/alpha/action
|
|
21
|
+
behavior. Name validity and descriptor geometry control visibility, layout and
|
|
22
|
+
touch bounds; image availability does not control them.
|
|
23
|
+
Detached binds and auto-height measurement use only known intrinsic metadata;
|
|
24
|
+
they MUST NOT submit named requests or read/decode SVG resources. Initial attach
|
|
25
|
+
starts loading without changing the measured glyph or button dimensions.
|
|
26
|
+
|
|
27
|
+
iOS original brand/opacity modes and the size overload's always-template behavior
|
|
28
|
+
remain. Android preserves Apple 16 × 20 width-based scaling and clipping by
|
|
29
|
+
requesting a proportionate raster and drawing at the old origin. Checkboxes
|
|
30
|
+
retain their 16 dp glyph plus 2 dp border offset in selector geometry, otherwise
|
|
31
|
+
80 percent centered geometry. Shared images are immutable; no cross-view mutable
|
|
32
|
+
Drawable bounds or pixel changes are allowed. Named work uses image's existing
|
|
33
|
+
two-worker and bounded cache service. Web consumers use its pure DOM external-icon
|
|
34
|
+
helper while keeping inherited color, action labels, anchors and exact sizes.
|
|
35
|
+
|
|
36
|
+
Acceptance must cover actual slots, first bind and repeated row reuse, unknown
|
|
37
|
+
and empty inputs, detach/reattach, resize/tint changes, both UIButton states,
|
|
38
|
+
checkboxes, Apple, original brands, legacy badge, and Web/desktop menus. All
|
|
39
|
+
static path readers must be removed from migrated routes; procedural drawings
|
|
40
|
+
and unrelated URI image loading retain their existing ownership.
|
|
41
|
+
|
|
3
42
|
This is the entry point for the package's behavioral contract. The linked
|
|
4
43
|
documents are part of this spec; implementation status is separate from target
|
|
5
44
|
architecture and runtime evidence.
|
|
@@ -51,6 +90,55 @@ ordinary press for that gesture. Motion cancellation, row rebind/recycle,
|
|
|
51
90
|
disabled state and disposal cancel pending recognition. Trailing accessory
|
|
52
91
|
actions remain independent. Omission preserves existing tap behavior.
|
|
53
92
|
|
|
93
|
+
### Native Swap token menu backport
|
|
94
|
+
|
|
95
|
+
Status: implemented in local native source, unreleased. Native build and runtime
|
|
96
|
+
acceptance are separate checks. Web and Desktop layout and actions stay unchanged;
|
|
97
|
+
their generated icon artwork is covered in the following section.
|
|
98
|
+
|
|
99
|
+
iOS and Android render `DotVerSolid` with the app's existing 24-unit solid icon
|
|
100
|
+
geometry. On Android, exactly two identity trailing accessories consisting of
|
|
101
|
+
`valuePair` followed by an `icon` named `DotVerSolid` with `actionKey: 'menu'`
|
|
102
|
+
lay out horizontally, matching iOS. Other icon names or action keys retain the
|
|
103
|
+
existing axis rules. This backport does not change row padding, accessory spacing,
|
|
104
|
+
button dimensions, actions, disabled state, or the public snapshot contract.
|
|
105
|
+
|
|
106
|
+
`NativeListAccessoryStackTests` checks the iOS image lookup and bound button image.
|
|
107
|
+
`NativeListRiskAccessoryTest` checks Android's actual icon drawing and the target
|
|
108
|
+
horizontal pair, including vertical controls on rebind.
|
|
109
|
+
|
|
110
|
+
### Built-in icon metadata generation
|
|
111
|
+
|
|
112
|
+
The closed `scripts/native-list-icons.json` manifest retains platform coverage,
|
|
113
|
+
canonical App source paths and iOS rendering strategy. `Circle` is a local
|
|
114
|
+
primitive. The NativeList-only legacy `BadgeVerifiedSolid` name maps to
|
|
115
|
+
`solid/badge-recognized.svg` / canonical `BadgeRecognizedSolid`, preserving its
|
|
116
|
+
thumbs-up artwork without adding a general alias.
|
|
117
|
+
|
|
118
|
+
`scripts/generate-icons.mjs --app-root <app-root>` matches each source uniquely
|
|
119
|
+
against image's generated catalog, validates source digests and manifest
|
|
120
|
+
metadata, then emits exactly three small Swift/Kotlin/Web name/geometry tables.
|
|
121
|
+
It writes no SVG, path data or xcassets; image owns the single resource pipeline.
|
|
122
|
+
All validation precedes writes. `--check` is read-only and detects missing or
|
|
123
|
+
ambiguous sources, invalid metadata and stale output. Stable sorting emits no
|
|
124
|
+
machine paths or timestamps. Frozen tests compare exact registry key sets;
|
|
125
|
+
this does not prove that every application-computed name is registered.
|
|
126
|
+
|
|
127
|
+
The closed manifest contains 43 names with iOS 42, Android 41 and Web 25
|
|
128
|
+
registry entries (including DotVerSolid). Document2Outline, WalletOutline and
|
|
129
|
+
PlusLargeOutline are registered on all three platforms; TicketOutline also
|
|
130
|
+
retains Web and adds native coverage. These main-line names use image-owned
|
|
131
|
+
canonical SVGs, not duplicate NativeList paths or asset catalogs.
|
|
132
|
+
Existing Activity ten-name and Market/Media private call sites resolve canonical
|
|
133
|
+
names through the same DOM helper; their previous coverage does not expand.
|
|
134
|
+
Image preserves raw path order, fill rules, fixed colors, opacity and viewBoxes.
|
|
135
|
+
NativeList retains its layout, button bounds, actions and snapshot interfaces.
|
|
136
|
+
|
|
137
|
+
Focused SDK fixture acceptance covers native attached lifecycle/rebinding,
|
|
138
|
+
legacy iOS asset comparisons, shared alpha semantics and real Web/Electron
|
|
139
|
+
source/lib consumers. This is not full App, balance, transaction or signed
|
|
140
|
+
release acceptance.
|
|
141
|
+
|
|
54
142
|
Identity trailing accessories stack vertically, except for the pairs every
|
|
55
143
|
platform lays out side by side: `value` + `checkbox`, `valuePair` + `menu`,
|
|
56
144
|
`valuePair` + an `icon` named `DotVerSolid` whose `actionKey` is `menu`,
|
|
@@ -61,13 +149,6 @@ platform lays out side by side: `value` + `checkbox`, `valuePair` + `menu`,
|
|
|
61
149
|
pair is the Swap token selector's balance column and row menu; stacking it
|
|
62
150
|
clipped both inside the fixed 60dp row on Android.
|
|
63
151
|
|
|
64
|
-
Icon names are a closed set drawn from the app's 24-unit Icon sources on every
|
|
65
|
-
platform (`iconPaths` on Android, `NativeListIcons.xcassets` on iOS,
|
|
66
|
-
`selectorIcons` on Web); an unmapped name renders an empty slot rather than a
|
|
67
|
-
placeholder. The set includes every name the app's NativeList builders pass,
|
|
68
|
-
including `DotVerSolid`, `Document2Outline`, `WalletOutline`, `PlusLargeOutline`
|
|
69
|
-
and `TicketOutline` (added 2026-10-30 after the OK-66072 audit found them missing
|
|
70
|
-
on native).
|
|
71
152
|
|
|
72
153
|
A `message` row may opt into `bodyLines: 0`; its complete body wraps within its
|
|
73
154
|
existing text area and determines natural height unless an explicit row or
|
|
@@ -2036,3 +2117,29 @@ A containing Web pager therefore applies insets, collapse/top commands and route
|
|
|
2036
2117
|
restoration to the owned host, with slot content as its padding target. The
|
|
2037
2118
|
ordinary no-slot viewport and explicit external-host paths keep their existing
|
|
2038
2119
|
ownership; no Pager dependency or new API is required.
|
|
2120
|
+
|
|
2121
|
+
### Shared icon recoloring revision — Implemented; scoped runtime acceptance
|
|
2122
|
+
|
|
2123
|
+
Android named glyph slots consume image's typed immutable shape-cache results:
|
|
2124
|
+
compatible fixed/single/same-alpha multiple colors update per-view tint without a
|
|
2125
|
+
request; new alpha or mixed-color raster requests preserve the last successful
|
|
2126
|
+
same-source/geometry bitmap until completion. Name/geometry changes, invalid,
|
|
2127
|
+
hidden/detach/recycle clear immediately and revisions reject old callbacks.
|
|
2128
|
+
Circle remains local; Apple and checkbox rectangles and coverage are unchanged.
|
|
2129
|
+
iOS retains its existing black template/original request and synchronous retint.
|
|
2130
|
+
Web React and DOM use the image package's demand-loaded external per-icon symbol
|
|
2131
|
+
URLs, with existing coverage/aliases/action labels and CSS color inheritance.
|
|
2132
|
+
|
|
2133
|
+
## Host-owned named resources — Implemented; SDK fixture runtime verified
|
|
2134
|
+
|
|
2135
|
+
Image provides decoding and the static Web `#onekey-icon-assets` provider boundary;
|
|
2136
|
+
the application owns complete named SVG resources. NativeList keeps its existing
|
|
2137
|
+
43-name platform coverage and three generated metadata tables, including its
|
|
2138
|
+
legacy `BadgeVerifiedSolid` to canonical `BadgeRecognizedSolid` mapping. No list
|
|
2139
|
+
props, naming coverage, geometry or rendering strategy changes.
|
|
2140
|
+
|
|
2141
|
+
The SDK metadata generator validates against the shared 63-sample example/test
|
|
2142
|
+
catalog without an App checkout, including raw source digests and viewBox sizes.
|
|
2143
|
+
It emits metadata only. Production consumers must supply the complete host icon
|
|
2144
|
+
bundle/assets and Web provider; SDK-only fixture tests prove their closed sample
|
|
2145
|
+
coverage, while the App's independent generator verifies all 1,909 resources.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import UIKit
|
|
2
2
|
|
|
3
3
|
// OneKey patch: explicit summary actions use the source text's physical-pixel line box.
|
|
4
|
-
final class NativeListAccessoryButton:
|
|
4
|
+
final class NativeListAccessoryButton: NativeListIconButton {
|
|
5
5
|
var pressedBackgroundColor: UIColor?
|
|
6
6
|
var normalBackgroundColor: UIColor = .clear
|
|
7
7
|
var fixedTextLineHeight: CGFloat?
|
|
@@ -197,7 +197,7 @@ final class NativeListAccessoryButton: UIButton {
|
|
|
197
197
|
final class NativeListAccessoryStack: UIStackView {
|
|
198
198
|
var fonts = NativeListFontFamilies()
|
|
199
199
|
private let accessoryButtons = (0..<2).map { _ in NativeListAccessoryButton(type: .system) }
|
|
200
|
-
private let checkboxButton =
|
|
200
|
+
private let checkboxButton = NativeListIconButton(type: .system)
|
|
201
201
|
private let spinner = UIActivityIndicatorView(style: .medium)
|
|
202
202
|
private var accessoryActions: [(String, NativeSelectionTarget?)] = []
|
|
203
203
|
private var checkboxAction: (String, NativeSelectionTarget?)?
|
|
@@ -250,6 +250,8 @@ final class NativeListAccessoryStack: UIStackView {
|
|
|
250
250
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String,
|
|
251
251
|
fonts: NativeListFontFamilies
|
|
252
252
|
) {
|
|
253
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
254
|
+
defer { iconUpdate.finish() }
|
|
253
255
|
self.fonts = fonts
|
|
254
256
|
reset()
|
|
255
257
|
currentItem = item
|
|
@@ -501,10 +503,7 @@ final class NativeListAccessoryStack: UIStackView {
|
|
|
501
503
|
state == "indeterminate"
|
|
502
504
|
? "CheckboxIndeterminateCustom"
|
|
503
505
|
: "CheckboxCheckedCustom"
|
|
504
|
-
checkboxButton.
|
|
505
|
-
state == "unchecked" ? nil : nativeListIcon(named: glyphName),
|
|
506
|
-
for: .normal
|
|
507
|
-
)
|
|
506
|
+
checkboxButton.bindIcon(state == "unchecked" ? nil : glyphName)
|
|
508
507
|
checkboxButton.tintColor = checkboxIconColor
|
|
509
508
|
// A disabled ListItem already applies 0.5 to its complete content. Avoid
|
|
510
509
|
// multiplying that opacity on the nested control a second time.
|
|
@@ -666,16 +665,7 @@ final class NativeListAccessoryStack: UIStackView {
|
|
|
666
665
|
button.tintColor = tintColor
|
|
667
666
|
let isDecorativeRefresh =
|
|
668
667
|
data.string("name") == "RefreshCcwOutline" && data.string("actionKey").isEmpty
|
|
669
|
-
|
|
670
|
-
? nativeListIcon(named: data.string("name"), size: CGSize(width: 16, height: 16))
|
|
671
|
-
: nativeListIcon(named: data.string("name"))
|
|
672
|
-
if let image {
|
|
673
|
-
button.setImage(image, for: .normal)
|
|
674
|
-
button.setImage(
|
|
675
|
-
image.withTintColor(tintColor, renderingMode: .alwaysOriginal),
|
|
676
|
-
for: .disabled
|
|
677
|
-
)
|
|
678
|
-
}
|
|
668
|
+
button.bindIcon(data.string("name"), size: isDecorativeRefresh ? CGSize(width: 16, height: 16) : nil)
|
|
679
669
|
let isDrillIn = data.string("kind") == "chevron"
|
|
680
670
|
let isTertiary = data.string("buttonStyle") == "tertiary"
|
|
681
671
|
if data.string("kind") == "icon" && isTertiary {
|
|
@@ -12,7 +12,7 @@ final class NativeListDataRowCell: NativeListRendererCell {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
private let visual = NativeListLeadingVisual()
|
|
15
|
-
private let favorite =
|
|
15
|
+
private let favorite = NativeListIconImageView()
|
|
16
16
|
private let accessories = NativeListAccessoryStack()
|
|
17
17
|
private let trailingAccessories = NativeListAccessoryStack()
|
|
18
18
|
private let columns = UIStackView()
|
|
@@ -51,6 +51,9 @@ final class NativeListDataRowCell: NativeListRendererCell {
|
|
|
51
51
|
_ item: NativeListItem, theme: [String: Any]?, layout: String,
|
|
52
52
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String
|
|
53
53
|
) {
|
|
54
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
55
|
+
defer { iconUpdate.finish() }
|
|
56
|
+
|
|
54
57
|
let data = item.data
|
|
55
58
|
let style = data.dictionary("style") ?? [:]
|
|
56
59
|
let image = style.dictionary("image") ?? [:]
|
|
@@ -64,8 +67,7 @@ final class NativeListDataRowCell: NativeListRendererCell {
|
|
|
64
67
|
let vp = CGFloat(style.double("verticalPadding", default: layout == "table" ? 10 : 8))
|
|
65
68
|
contentInsets = UIEdgeInsets(top: vp, left: hp, bottom: vp, right: hp)
|
|
66
69
|
favorite.isHidden = !data.bool("favorite") && !data.bool("favoriteActive")
|
|
67
|
-
favorite.
|
|
68
|
-
named: data.bool("favoriteActive") ? "StarSolid" : "StarOutline")
|
|
70
|
+
favorite.bindIcon(data.bool("favoriteActive") ? "StarSolid" : "StarOutline")
|
|
69
71
|
favorite.tintColor = nativeListColor(
|
|
70
72
|
theme, data.bool("favoriteActive") ? "icon" : "iconSubdued",
|
|
71
73
|
data.bool("favoriteActive") ? "#646464" : "#8D8D8D")
|
|
@@ -166,13 +168,18 @@ final class NativeListDataRowCell: NativeListRendererCell {
|
|
|
166
168
|
NSLayoutConstraint.activate(columnWidthConstraints)
|
|
167
169
|
columnWeights = normalizedWeights
|
|
168
170
|
}
|
|
171
|
+
|
|
169
172
|
}
|
|
170
173
|
override func bindSelectionContent(
|
|
171
174
|
_ item: NativeListItem,
|
|
172
175
|
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String
|
|
173
176
|
) {
|
|
177
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
178
|
+
defer { iconUpdate.finish() }
|
|
179
|
+
|
|
174
180
|
accessories.updateSelection(item, checkboxState: checkboxState)
|
|
175
181
|
trailingAccessories.updateSelection(item, checkboxState: checkboxState)
|
|
182
|
+
|
|
176
183
|
}
|
|
177
184
|
override func recycleContent() {
|
|
178
185
|
visual.recycle()
|
|
@@ -185,8 +192,8 @@ final class NativeListDataRowCell: NativeListRendererCell {
|
|
|
185
192
|
private final class NativeListTableColumnView: UIStackView {
|
|
186
193
|
private let primaryLine = UIStackView()
|
|
187
194
|
private let primaryLabel = NativeListTextLabel()
|
|
188
|
-
private let titleIconView =
|
|
189
|
-
private let secondaryTitleIconView =
|
|
195
|
+
private let titleIconView = NativeListIconImageView()
|
|
196
|
+
private let secondaryTitleIconView = NativeListIconImageView()
|
|
190
197
|
private let badgesStack = UIStackView()
|
|
191
198
|
private let secondaryLine = UIStackView()
|
|
192
199
|
private let secondaryLeadingLabel = NativeListTextLabel()
|
|
@@ -280,7 +287,7 @@ private final class NativeListTableColumnView: UIStackView {
|
|
|
280
287
|
for (index, descriptor) in descriptors.enumerated() {
|
|
281
288
|
guard let descriptor else { continue }
|
|
282
289
|
let icon = index == 0 ? titleIconView : secondaryTitleIconView
|
|
283
|
-
icon.
|
|
290
|
+
icon.bindIcon(descriptor.string("name"))
|
|
284
291
|
icon.tintColor = UIColor(
|
|
285
292
|
nativeListHex: descriptor.string("tintColor",
|
|
286
293
|
default: theme?["iconSubdued"] as? String ?? "#8D8D8D"),
|
|
@@ -303,6 +310,8 @@ private final class NativeListTableColumnView: UIStackView {
|
|
|
303
310
|
theme: [String: Any]?,
|
|
304
311
|
fonts: NativeListFontFamilies
|
|
305
312
|
) {
|
|
313
|
+
let iconUpdate = NativeListIconUpdate(self)
|
|
314
|
+
defer { iconUpdate.finish() }
|
|
306
315
|
reset()
|
|
307
316
|
let textAlignment: NSTextAlignment
|
|
308
317
|
switch column.string("alignment") {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import CoreText
|
|
2
|
+
import OneKeyImage
|
|
2
3
|
import UIKit
|
|
3
4
|
|
|
4
|
-
private final class NativeListResourceToken {}
|
|
5
|
-
|
|
6
5
|
enum NativeListFontWeight {
|
|
7
6
|
case regular
|
|
8
7
|
case medium
|
|
@@ -19,19 +18,6 @@ enum NativeListFontWeight {
|
|
|
19
18
|
}
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
private enum NativeListResources {
|
|
23
|
-
static let bundle: Bundle = {
|
|
24
|
-
let owners = [Bundle(for: NativeListResourceToken.self), .main]
|
|
25
|
-
for owner in owners {
|
|
26
|
-
if let url = owner.url(forResource: "NativeListResources", withExtension: "bundle"),
|
|
27
|
-
let bundle = Bundle(url: url) {
|
|
28
|
-
return bundle
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return Bundle(for: NativeListResourceToken.self)
|
|
32
|
-
}()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
21
|
struct NativeListFontFamilies: Equatable {
|
|
36
22
|
let names: [String: String]
|
|
37
23
|
init(names: [String: String] = [:]) { self.names = names }
|
|
@@ -84,76 +70,19 @@ func nativeListTabularFont(
|
|
|
84
70
|
descriptor: font.fontDescriptor.addingAttributes([.featureSettings: settings]), size: size)
|
|
85
71
|
}
|
|
86
72
|
|
|
87
|
-
func
|
|
88
|
-
// OneKey patch: the connection indicator is a solid circle at its requested size.
|
|
73
|
+
func nativeListIconMetadata(named name: String) -> NativeListIconMetadata? {
|
|
89
74
|
if name == "Circle" {
|
|
90
|
-
return
|
|
91
|
-
UIColor.black.setFill()
|
|
92
|
-
UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 24, height: 24)).fill()
|
|
93
|
-
}.withRenderingMode(.alwaysTemplate)
|
|
75
|
+
return NativeListIconMetadata(name: "Circle", size: CGSize(width: 24, height: 24), original: false)
|
|
94
76
|
}
|
|
95
|
-
let
|
|
96
|
-
|
|
97
|
-
// OneKey patch: official selector icon geometry.
|
|
98
|
-
case "AccountErrorCustom": assetName = "onekey_selector_account_error_custom"
|
|
99
|
-
// OneKey patch: official selector icon geometry.
|
|
100
|
-
case "CrossedSmallSolid": assetName = "onekey_selector_crossed_small_solid"
|
|
101
|
-
// OneKey patch: official selector icon geometry.
|
|
102
|
-
case "AllNetworksSolid": assetName = "onekey_selector_all_networks_solid"
|
|
103
|
-
// OneKey patch: official selector icon geometry.
|
|
104
|
-
case "BotIllus": assetName = "onekey_selector_bot_illus"
|
|
105
|
-
// OneKey patch: official selector icon geometry.
|
|
106
|
-
case "AppleBrand": assetName = "onekey_selector_apple_brand"
|
|
107
|
-
// OneKey patch: official selector icon geometry.
|
|
108
|
-
case "GoogleIllus": assetName = "onekey_selector_google_illus"
|
|
109
|
-
// OneKey patch: official selector icon geometry.
|
|
110
|
-
case "LockSolid": assetName = "onekey_selector_lock_solid"
|
|
111
|
-
// OneKey patch: official selector icon geometry.
|
|
112
|
-
case "GlobusOutline": assetName = "onekey_selector_globus_outline"
|
|
113
|
-
case "GasSolid": assetName = "onekey_gas_solid"
|
|
114
|
-
case "RefreshCcwOutline": assetName = "onekey_refresh_ccw_outline"
|
|
115
|
-
case "CryptoCoinOutline": assetName = "onekey_crypto_coin_outline"
|
|
116
|
-
case "ArrowBottomOutline": assetName = "onekey_arrow_bottom"
|
|
117
|
-
case "ArrowTopOutline": assetName = "onekey_arrow_top"
|
|
118
|
-
case "ChartTrendingUpOutline": assetName = "onekey_chart_trending_up"
|
|
119
|
-
case "SwapHorOutline": assetName = "onekey_swap_hor"
|
|
120
|
-
case "ShieldExclamationOutline": assetName = "onekey_shield_exclamation"
|
|
121
|
-
case "InfoCircleOutline": assetName = "onekey_info_circle"
|
|
122
|
-
case "SpeakerPromoteOutline": assetName = "onekey_speaker_promote"
|
|
123
|
-
case "PlusCircleOutline": assetName = "onekey_plus_circle"
|
|
124
|
-
case "PlusSmallOutline": assetName = "onekey_plus_small"
|
|
125
|
-
case "DotHorOutline": assetName = "onekey_dot_hor"
|
|
126
|
-
case "PencilOutline": assetName = "onekey_pencil"
|
|
127
|
-
case "MinusCircleSolid": assetName = "onekey_minus_circle_solid"
|
|
128
|
-
case "MinusCircleOutline": assetName = "onekey_minus_circle"
|
|
129
|
-
case "ChevronRightSmallOutline": assetName = "onekey_chevron_right_small"
|
|
130
|
-
case "DragOutline": assetName = "onekey_drag"
|
|
131
|
-
case "StarOutline": assetName = "onekey_star"
|
|
132
|
-
case "StarSolid": assetName = "onekey_star_solid"
|
|
133
|
-
case "BadgeVerifiedSolid": assetName = "onekey_badge_verified_solid"
|
|
134
|
-
case "ChevronGrabberVerOutline": assetName = "onekey_chevron_grabber_ver"
|
|
135
|
-
case "ChevronBottomOutline": assetName = "onekey_chevron_bottom"
|
|
136
|
-
case "ChevronTopOutline": assetName = "onekey_chevron_top"
|
|
137
|
-
case "CheckboxCheckedCustom": assetName = "onekey_checkbox_checked"
|
|
138
|
-
case "CheckboxIndeterminateCustom": assetName = "onekey_checkbox_indeterminate"
|
|
139
|
-
case "ErrorSolid": assetName = "onekey_error_solid"
|
|
140
|
-
case "QuestionmarkSolid": assetName = "onekey_questionmark_solid"
|
|
141
|
-
case "ImageSquareWavesOutline": assetName = "onekey_image_square_waves"
|
|
142
|
-
// Asset-list icons the app passes by name (OK-66072 audit): unmapped names rendered nothing.
|
|
143
|
-
case "DotVerSolid": assetName = "onekey_dot_ver_solid"
|
|
144
|
-
case "Document2Outline": assetName = "onekey_document2"
|
|
145
|
-
case "WalletOutline": assetName = "onekey_wallet"
|
|
146
|
-
case "PlusLargeOutline": assetName = "onekey_plus_large"
|
|
147
|
-
case "TicketOutline": assetName = "onekey_ticket"
|
|
148
|
-
default: return nil
|
|
77
|
+
if let generated = NativeListGeneratedIcons.icons[name] {
|
|
78
|
+
return generated
|
|
149
79
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}.withRenderingMode(.alwaysTemplate)
|
|
80
|
+
guard let meta = OneKeyNamedSVG.shared.iconMetadata(forName: name) else {
|
|
81
|
+
return nil
|
|
82
|
+
}
|
|
83
|
+
return NativeListIconMetadata(
|
|
84
|
+
name: name,
|
|
85
|
+
size: CGSize(width: meta.width, height: meta.height),
|
|
86
|
+
original: meta.recolorMode == .fixed || meta.recolorMode == .raster
|
|
87
|
+
)
|
|
159
88
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Generated by scripts/generate-icons.mjs. Coverage and legacy names only; hosts provide SVG resources.
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
struct NativeListIconMetadata {
|
|
5
|
+
let name: String
|
|
6
|
+
let size: CGSize
|
|
7
|
+
let original: Bool
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
enum NativeListGeneratedIcons {
|
|
11
|
+
static let icons: [String: NativeListIconMetadata] = [
|
|
12
|
+
"AccountErrorCustom": NativeListIconMetadata(name: "AccountErrorCustom", size: CGSize(width: 18, height: 18), original: true),
|
|
13
|
+
"AllNetworksSolid": NativeListIconMetadata(name: "AllNetworksSolid", size: CGSize(width: 24, height: 24), original: false),
|
|
14
|
+
"AppleBrand": NativeListIconMetadata(name: "AppleBrand", size: CGSize(width: 16, height: 20), original: false),
|
|
15
|
+
"ArrowBottomOutline": NativeListIconMetadata(name: "ArrowBottomOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
16
|
+
"ArrowTopOutline": NativeListIconMetadata(name: "ArrowTopOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
17
|
+
"BadgeVerifiedSolid": NativeListIconMetadata(name: "BadgeRecognizedSolid", size: CGSize(width: 24, height: 24), original: false),
|
|
18
|
+
"BotIllus": NativeListIconMetadata(name: "BotIllus", size: CGSize(width: 24, height: 24), original: true),
|
|
19
|
+
"ChartTrendingUpOutline": NativeListIconMetadata(name: "ChartTrendingUpOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
20
|
+
"CheckboxCheckedCustom": NativeListIconMetadata(name: "CheckboxCheckedCustom", size: CGSize(width: 16, height: 16), original: false),
|
|
21
|
+
"CheckboxIndeterminateCustom": NativeListIconMetadata(name: "CheckboxIndeterminateCustom", size: CGSize(width: 16, height: 16), original: false),
|
|
22
|
+
"ChevronBottomOutline": NativeListIconMetadata(name: "ChevronBottomOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
23
|
+
"ChevronGrabberVerOutline": NativeListIconMetadata(name: "ChevronGrabberVerOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
24
|
+
"ChevronRightSmallOutline": NativeListIconMetadata(name: "ChevronRightSmallOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
25
|
+
"ChevronTopOutline": NativeListIconMetadata(name: "ChevronTopOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
26
|
+
"CrossedSmallSolid": NativeListIconMetadata(name: "CrossedSmallSolid", size: CGSize(width: 24, height: 24), original: false),
|
|
27
|
+
"CryptoCoinOutline": NativeListIconMetadata(name: "CryptoCoinOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
28
|
+
"Document2Outline": NativeListIconMetadata(name: "Document2Outline", size: CGSize(width: 24, height: 24), original: false),
|
|
29
|
+
"DotHorOutline": NativeListIconMetadata(name: "DotHorOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
30
|
+
"DotVerSolid": NativeListIconMetadata(name: "DotVerSolid", size: CGSize(width: 24, height: 24), original: false),
|
|
31
|
+
"DragOutline": NativeListIconMetadata(name: "DragOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
32
|
+
"ErrorSolid": NativeListIconMetadata(name: "ErrorSolid", size: CGSize(width: 24, height: 24), original: false),
|
|
33
|
+
"GasSolid": NativeListIconMetadata(name: "GasSolid", size: CGSize(width: 24, height: 24), original: false),
|
|
34
|
+
"GlobusOutline": NativeListIconMetadata(name: "GlobusOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
35
|
+
"GoogleIllus": NativeListIconMetadata(name: "GoogleIllus", size: CGSize(width: 24, height: 24), original: true),
|
|
36
|
+
"ImageSquareWavesOutline": NativeListIconMetadata(name: "ImageSquareWavesOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
37
|
+
"InfoCircleOutline": NativeListIconMetadata(name: "InfoCircleOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
38
|
+
"LockSolid": NativeListIconMetadata(name: "LockSolid", size: CGSize(width: 24, height: 24), original: false),
|
|
39
|
+
"MinusCircleOutline": NativeListIconMetadata(name: "MinusCircleOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
40
|
+
"MinusCircleSolid": NativeListIconMetadata(name: "MinusCircleSolid", size: CGSize(width: 24, height: 24), original: false),
|
|
41
|
+
"PencilOutline": NativeListIconMetadata(name: "PencilOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
42
|
+
"PlusCircleOutline": NativeListIconMetadata(name: "PlusCircleOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
43
|
+
"PlusLargeOutline": NativeListIconMetadata(name: "PlusLargeOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
44
|
+
"PlusSmallOutline": NativeListIconMetadata(name: "PlusSmallOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
45
|
+
"QuestionmarkSolid": NativeListIconMetadata(name: "QuestionmarkSolid", size: CGSize(width: 24, height: 24), original: false),
|
|
46
|
+
"RefreshCcwOutline": NativeListIconMetadata(name: "RefreshCcwOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
47
|
+
"ShieldExclamationOutline": NativeListIconMetadata(name: "ShieldExclamationOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
48
|
+
"SpeakerPromoteOutline": NativeListIconMetadata(name: "SpeakerPromoteOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
49
|
+
"StarOutline": NativeListIconMetadata(name: "StarOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
50
|
+
"StarSolid": NativeListIconMetadata(name: "StarSolid", size: CGSize(width: 24, height: 24), original: false),
|
|
51
|
+
"SwapHorOutline": NativeListIconMetadata(name: "SwapHorOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
52
|
+
"TicketOutline": NativeListIconMetadata(name: "TicketOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
53
|
+
"WalletOutline": NativeListIconMetadata(name: "WalletOutline", size: CGSize(width: 24, height: 24), original: false),
|
|
54
|
+
]
|
|
55
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import OneKeyImage
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/// One request owner per glyph slot; metadata also supplies detached measurement.
|
|
5
|
+
private final class NativeListIconBinding {
|
|
6
|
+
private weak var view: UIView?
|
|
7
|
+
private let owner = NSObject()
|
|
8
|
+
private let apply: (UIImage?) -> Void
|
|
9
|
+
private var descriptor: NativeListIconMetadata?
|
|
10
|
+
private var pointSize = CGSize.zero
|
|
11
|
+
private var template = true
|
|
12
|
+
private var sizeOverride: CGSize?
|
|
13
|
+
private var currentImage: UIImage?
|
|
14
|
+
private var placeholder: UIImage?
|
|
15
|
+
private var updateDepth = 0
|
|
16
|
+
private var pendingClear = false
|
|
17
|
+
private var revision: UInt64 = 0
|
|
18
|
+
private var identity: String?
|
|
19
|
+
|
|
20
|
+
init(view: UIView, apply: @escaping (UIImage?) -> Void) { self.view = view; self.apply = apply }
|
|
21
|
+
|
|
22
|
+
func bind(_ name: String?, size: CGSize? = nil) {
|
|
23
|
+
guard let name, let metadata = nativeListIconMetadata(named: name) else {
|
|
24
|
+
clearNow(); return
|
|
25
|
+
}
|
|
26
|
+
if descriptor?.name == metadata.name, descriptor?.original == metadata.original,
|
|
27
|
+
descriptor?.size == metadata.size, sizeOverride == size {
|
|
28
|
+
pendingClear = false
|
|
29
|
+
apply(currentImage)
|
|
30
|
+
refresh(); return
|
|
31
|
+
}
|
|
32
|
+
clearNow()
|
|
33
|
+
descriptor = metadata
|
|
34
|
+
pointSize = size ?? metadata.size
|
|
35
|
+
sizeOverride = size
|
|
36
|
+
template = size != nil || !metadata.original
|
|
37
|
+
let format = UIGraphicsImageRendererFormat(); format.scale = 1
|
|
38
|
+
placeholder = UIGraphicsImageRenderer(size: pointSize, format: format).image { _ in }
|
|
39
|
+
.withRenderingMode(template ? .alwaysTemplate : .alwaysOriginal)
|
|
40
|
+
currentImage = placeholder
|
|
41
|
+
apply(currentImage)
|
|
42
|
+
refresh()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func clear(display: Bool = true) {
|
|
46
|
+
if display, updateDepth > 0 {
|
|
47
|
+
pendingClear = true; apply(nil); return
|
|
48
|
+
}
|
|
49
|
+
clearNow(display: display)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private func clearNow(display: Bool = true) {
|
|
53
|
+
revision &+= 1; identity = nil; descriptor = nil; currentImage = nil; placeholder = nil
|
|
54
|
+
pendingClear = false
|
|
55
|
+
OneKeyNamedSVG.shared.cancel(owner: owner)
|
|
56
|
+
if display { apply(nil) }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func beginUpdate() { updateDepth += 1 }
|
|
60
|
+
func endUpdate() {
|
|
61
|
+
updateDepth -= 1
|
|
62
|
+
guard updateDepth == 0 else { return }
|
|
63
|
+
if pendingClear { clearNow() } else { refresh() }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func retint() { if descriptor != nil && !pendingClear { apply(currentImage) } }
|
|
67
|
+
|
|
68
|
+
func refresh(force: Bool = false) {
|
|
69
|
+
guard updateDepth == 0, let view, let descriptor else { return }
|
|
70
|
+
let attached = view.window != nil && !view.isHidden && !view.bounds.isEmpty
|
|
71
|
+
let screenScale = max(1, view.window?.screen.scale ?? view.traitCollection.displayScale)
|
|
72
|
+
let fit = min(view.bounds.width / pointSize.width, view.bounds.height / pointSize.height)
|
|
73
|
+
let rasterScale = ceil(screenScale * max(1, fit))
|
|
74
|
+
let next = "\(descriptor.name)|\(pointSize)|\(rasterScale)|\(screenScale)|\(attached)|\(template)"
|
|
75
|
+
guard force || next != identity else { return }
|
|
76
|
+
identity = next; revision &+= 1
|
|
77
|
+
let generation = revision
|
|
78
|
+
OneKeyNamedSVG.shared.cancel(owner: owner)
|
|
79
|
+
currentImage = placeholder
|
|
80
|
+
apply(currentImage)
|
|
81
|
+
guard attached else { return }
|
|
82
|
+
if descriptor.name == "Circle" {
|
|
83
|
+
let image = UIGraphicsImageRenderer(size: pointSize).image { _ in
|
|
84
|
+
UIColor.black.setFill(); UIBezierPath(ovalIn: CGRect(origin: .zero, size: pointSize)).fill()
|
|
85
|
+
}
|
|
86
|
+
currentImage = image.withRenderingMode(.alwaysTemplate); apply(currentImage); return
|
|
87
|
+
}
|
|
88
|
+
let sourceSize = descriptor.size
|
|
89
|
+
let sourceScale = sizeOverride == nil ? rasterScale : ceil(rasterScale * max(pointSize.width / sourceSize.width, pointSize.height / sourceSize.height))
|
|
90
|
+
let pixelWidth = ceil(sourceSize.width * sourceScale), pixelHeight = ceil(sourceSize.height * sourceScale)
|
|
91
|
+
guard pixelWidth.isFinite, pixelHeight.isFinite, pixelWidth > 0, pixelHeight > 0,
|
|
92
|
+
pixelWidth <= 4096, pixelHeight <= 4096, pixelWidth * pixelHeight <= 4_194_304 else { return }
|
|
93
|
+
OneKeyNamedSVG.shared.request(owner: owner, name: descriptor.name, color: "rgba(0,0,0,1)",
|
|
94
|
+
width: Int(pixelWidth), height: Int(pixelHeight),
|
|
95
|
+
scale: screenScale) { [weak self] image in
|
|
96
|
+
guard let self, self.revision == generation, let view = self.view,
|
|
97
|
+
view.window != nil, !view.isHidden else { return }
|
|
98
|
+
guard let cgImage = image?.cgImage else { return }
|
|
99
|
+
var result = UIImage(cgImage: cgImage, scale: sourceScale, orientation: .up)
|
|
100
|
+
if let size = self.sizeOverride {
|
|
101
|
+
let format = UIGraphicsImageRendererFormat(); format.scale = rasterScale
|
|
102
|
+
result = UIGraphicsImageRenderer(size: size, format: format).image { _ in
|
|
103
|
+
result.draw(in: CGRect(origin: .zero, size: size))
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
self.currentImage = result.withRenderingMode(self.template ? .alwaysTemplate : .alwaysOriginal)
|
|
107
|
+
self.apply(self.currentImage)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
deinit { OneKeyNamedSVG.shared.cancel(owner: owner) }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
final class NativeListIconImageView: UIImageView {
|
|
115
|
+
private var applying = false
|
|
116
|
+
private lazy var iconBinding = NativeListIconBinding(view: self) { [weak self] image in
|
|
117
|
+
guard let self else { return }
|
|
118
|
+
self.applying = true; self.image = image; self.applying = false
|
|
119
|
+
}
|
|
120
|
+
func bindIcon(_ name: String?, size: CGSize? = nil) { iconBinding.bind(name, size: size) }
|
|
121
|
+
func beginIconUpdate() { iconBinding.beginUpdate() }
|
|
122
|
+
func endIconUpdate() { iconBinding.endUpdate() }
|
|
123
|
+
override var image: UIImage? { didSet { if !applying { iconBinding.clear(display: image == nil) } } }
|
|
124
|
+
override var isHidden: Bool { didSet { iconBinding.refresh() } }
|
|
125
|
+
override func layoutSubviews() { super.layoutSubviews(); iconBinding.refresh() }
|
|
126
|
+
override func didMoveToWindow() { super.didMoveToWindow(); iconBinding.refresh() }
|
|
127
|
+
override func tintColorDidChange() { super.tintColorDidChange(); iconBinding.retint() }
|
|
128
|
+
override func traitCollectionDidChange(_ previous: UITraitCollection?) {
|
|
129
|
+
super.traitCollectionDidChange(previous); iconBinding.refresh()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
class NativeListIconButton: UIButton {
|
|
134
|
+
private var applying = false
|
|
135
|
+
private lazy var iconBinding = NativeListIconBinding(view: self) { [weak self] image in
|
|
136
|
+
guard let self else { return }
|
|
137
|
+
self.applying = true
|
|
138
|
+
self.setImage(image, for: .normal)
|
|
139
|
+
self.setImage(image?.withTintColor(self.tintColor, renderingMode: .alwaysOriginal), for: .disabled)
|
|
140
|
+
self.applying = false
|
|
141
|
+
}
|
|
142
|
+
func bindIcon(_ name: String?, size: CGSize? = nil) { iconBinding.bind(name, size: size) }
|
|
143
|
+
func beginIconUpdate() { iconBinding.beginUpdate() }
|
|
144
|
+
func endIconUpdate() { iconBinding.endUpdate() }
|
|
145
|
+
override func setImage(_ image: UIImage?, for state: UIControl.State) {
|
|
146
|
+
if !applying && state == .normal { iconBinding.clear(display: image == nil) }
|
|
147
|
+
super.setImage(image, for: state)
|
|
148
|
+
}
|
|
149
|
+
override var isHidden: Bool { didSet { iconBinding.refresh() } }
|
|
150
|
+
override func layoutSubviews() { super.layoutSubviews(); iconBinding.refresh() }
|
|
151
|
+
override func didMoveToWindow() { super.didMoveToWindow(); iconBinding.refresh() }
|
|
152
|
+
override func tintColorDidChange() { super.tintColorDidChange(); iconBinding.retint() }
|
|
153
|
+
override func traitCollectionDidChange(_ previous: UITraitCollection?) {
|
|
154
|
+
super.traitCollectionDidChange(previous); iconBinding.refresh()
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// A synchronous renderer bind may clear then restore the same glyph descriptor.
|
|
159
|
+
final class NativeListIconUpdate {
|
|
160
|
+
private var images: [NativeListIconImageView] = []
|
|
161
|
+
private var buttons: [NativeListIconButton] = []
|
|
162
|
+
init(_ root: UIView) {
|
|
163
|
+
func collect(_ view: UIView) {
|
|
164
|
+
if let image = view as? NativeListIconImageView { images.append(image); image.beginIconUpdate() }
|
|
165
|
+
if let button = view as? NativeListIconButton { buttons.append(button); button.beginIconUpdate() }
|
|
166
|
+
view.subviews.forEach(collect)
|
|
167
|
+
}
|
|
168
|
+
collect(root)
|
|
169
|
+
}
|
|
170
|
+
func finish() { images.forEach { $0.endIconUpdate() }; buttons.forEach { $0.endIconUpdate() } }
|
|
171
|
+
}
|