-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathview.py
More file actions
173 lines (156 loc) · 6.38 KB
/
view.py
File metadata and controls
173 lines (156 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
from abc import ABC
from typing import Any, Optional, Tuple
# ========================================
# Base class
# ========================================
class ViewBase(ABC):
def __init__(self) -> None:
# Native bridge handles return types dynamically; these attributes are set at runtime.
self.native_instance: Any = None
self.native_class: Any = None
# Record margins for parents that can apply them (Android LinearLayout)
self._pn_margin: Optional[Tuple[int, int, int, int]] = None
# ========================================
# Lightweight style helpers
# ========================================
def set_background_color(self, color: Any) -> "ViewBase":
"""Set background color. Accepts platform color int or CSS-like hex string. Returns self."""
try:
from .utils import IS_ANDROID
if isinstance(color, str):
# Support formats: #RRGGBB or #AARRGGBB
c = color.strip()
if c.startswith("#"):
c = c[1:]
if len(c) == 6:
c = "FF" + c
color_int = int(c, 16)
else:
color_int = int(color)
if IS_ANDROID:
# Android expects ARGB int
self.native_instance.setBackgroundColor(color_int)
else:
# iOS expects a UIColor
from rubicon.objc import ObjCClass
UIColor = ObjCClass("UIColor")
a = ((color_int >> 24) & 0xFF) / 255.0
r = ((color_int >> 16) & 0xFF) / 255.0
g = ((color_int >> 8) & 0xFF) / 255.0
b = (color_int & 0xFF) / 255.0
try:
color_obj = UIColor.colorWithRed_green_blue_alpha_(r, g, b, a)
except Exception:
color_obj = UIColor.blackColor()
try:
self.native_instance.setBackgroundColor_(color_obj)
except Exception:
try:
# Some UIKit classes expose 'backgroundColor' property
self.native_instance.setBackgroundColor_(color_obj)
except Exception:
pass
except Exception:
pass
return self
def set_padding(
self,
left: Optional[int] = None,
top: Optional[int] = None,
right: Optional[int] = None,
bottom: Optional[int] = None,
all: Optional[int] = None,
horizontal: Optional[int] = None,
vertical: Optional[int] = None,
) -> "ViewBase":
"""Set padding (dp on Android; best-effort on iOS where supported). Returns self.
When provided, 'all' applies to all sides; 'horizontal' applies to left and right;
'vertical' applies to top and bottom; individual overrides take precedence.
"""
try:
from .utils import IS_ANDROID, get_android_context
left_value = left
top_value = top
right_value = right
bottom_value = bottom
if all is not None:
left_value = top_value = right_value = bottom_value = all
if horizontal is not None:
left_value = horizontal if left_value is None else left_value
right_value = horizontal if right_value is None else right_value
if vertical is not None:
top_value = vertical if top_value is None else top_value
bottom_value = vertical if bottom_value is None else bottom_value
left_value = left_value or 0
top_value = top_value or 0
right_value = right_value or 0
bottom_value = bottom_value or 0
if IS_ANDROID:
density = get_android_context().getResources().getDisplayMetrics().density
lpx = int(left_value * density)
tpx = int(top_value * density)
rpx = int(right_value * density)
bpx = int(bottom_value * density)
self.native_instance.setPadding(lpx, tpx, rpx, bpx)
else:
# Best-effort: many UIKit views don't have direct padding; leave to containers (e.g. UIStackView)
# No-op by default.
pass
except Exception:
pass
return self
def set_margin(
self,
left: Optional[int] = None,
top: Optional[int] = None,
right: Optional[int] = None,
bottom: Optional[int] = None,
all: Optional[int] = None,
horizontal: Optional[int] = None,
vertical: Optional[int] = None,
) -> "ViewBase":
"""Record margins for this view (applied where supported). Returns self.
Currently applied automatically when added to Android LinearLayout (StackView).
"""
try:
left_value = left
top_value = top
right_value = right
bottom_value = bottom
if all is not None:
left_value = top_value = right_value = bottom_value = all
if horizontal is not None:
left_value = horizontal if left_value is None else left_value
right_value = horizontal if right_value is None else right_value
if vertical is not None:
top_value = vertical if top_value is None else top_value
bottom_value = vertical if bottom_value is None else bottom_value
left_value = int(left_value or 0)
top_value = int(top_value or 0)
right_value = int(right_value or 0)
bottom_value = int(bottom_value or 0)
self._pn_margin = (left_value, top_value, right_value, bottom_value)
except Exception:
pass
return self
def wrap_in_scroll(self) -> Any:
"""Return a ScrollView containing this view as its only child. Returns the ScrollView."""
try:
# Local import to avoid circulars
from .scroll_view import ScrollView
sv = ScrollView()
sv.add_view(self)
return sv
except Exception:
return None
# @abstractmethod
# def add_view(self, view):
# pass
#
# @abstractmethod
# def set_layout(self, layout):
# pass
#
# @abstractmethod
# def show(self):
# pass