Skip to content

Commit b740e14

Browse files
Document DisplayMetrics
1 parent 978c0c2 commit b740e14

File tree

2 files changed

+330
-0
lines changed

2 files changed

+330
-0
lines changed

docs/frameworks/display-metrics.md

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
# DisplayMetrics
2+
3+
DisplayMetrics is an Android-inspired singleton class that provides a unified, clean API for accessing display properties like width, height, DPI, and pointer coordinates.
4+
5+
## Overview
6+
7+
Instead of scattered module-level functions, DisplayMetrics centralizes all display-related metrics in a single class with class methods. This provides:
8+
9+
- **Unified API** - Single class for all display metrics
10+
- **Clean Namespace** - No scattered functions cluttering imports
11+
- **Testable** - DisplayMetrics can be tested independently
12+
- **Android-Inspired** - Follows Android's DisplayMetrics pattern
13+
- **Elegant Initialization** - Setters called from `init_rootscreen()`
14+
15+
## Architecture
16+
17+
DisplayMetrics is implemented as a singleton using class variables and class methods:
18+
19+
```python
20+
class DisplayMetrics:
21+
_width = None
22+
_height = None
23+
_dpi = None
24+
25+
@classmethod
26+
def set_resolution(cls, width, height):
27+
"""Set the display resolution (called by init_rootscreen)."""
28+
cls._width = width
29+
cls._height = height
30+
31+
@classmethod
32+
def width(cls):
33+
"""Get display width in pixels."""
34+
return cls._width
35+
36+
# ... other methods
37+
```
38+
39+
No instance creation is needed - all methods are class methods that operate on class variables.
40+
41+
## Usage
42+
43+
### Basic Display Metrics
44+
45+
```python
46+
from mpos import DisplayMetrics
47+
48+
# Get display dimensions
49+
width = DisplayMetrics.width() # e.g., 320
50+
height = DisplayMetrics.height() # e.g., 240
51+
dpi = DisplayMetrics.dpi() # e.g., 130
52+
```
53+
54+
### Percentage-Based Calculations
55+
56+
```python
57+
from mpos import DisplayMetrics
58+
59+
# Get percentage of display width/height
60+
half_width = DisplayMetrics.pct_of_width(50) # 50% of width
61+
quarter_height = DisplayMetrics.pct_of_height(25) # 25% of height
62+
```
63+
64+
### Dimension Queries
65+
66+
```python
67+
from mpos import DisplayMetrics
68+
69+
# Get min/max dimensions
70+
min_dim = DisplayMetrics.min_dimension() # min(width, height)
71+
max_dim = DisplayMetrics.max_dimension() # max(width, height)
72+
```
73+
74+
### Pointer/Touch Coordinates
75+
76+
```python
77+
from mpos import DisplayMetrics
78+
79+
# Get current pointer/touch position
80+
x, y = DisplayMetrics.pointer_xy()
81+
if x == -1 and y == -1:
82+
print("No active input device")
83+
else:
84+
print(f"Pointer at ({x}, {y})")
85+
```
86+
87+
## API Reference
88+
89+
### Class Methods
90+
91+
#### `set_resolution(width, height)`
92+
Set the display resolution. Called by `init_rootscreen()` during initialization.
93+
94+
**Parameters:**
95+
- `width` (int): Display width in pixels
96+
- `height` (int): Display height in pixels
97+
98+
**Example:**
99+
```python
100+
DisplayMetrics.set_resolution(320, 240)
101+
```
102+
103+
#### `set_dpi(dpi)`
104+
Set the display DPI. Called by `init_rootscreen()` during initialization.
105+
106+
**Parameters:**
107+
- `dpi` (int): Display DPI (dots per inch)
108+
109+
**Example:**
110+
```python
111+
DisplayMetrics.set_dpi(130)
112+
```
113+
114+
#### `width()`
115+
Get display width in pixels.
116+
117+
**Returns:** int - Display width
118+
119+
**Example:**
120+
```python
121+
w = DisplayMetrics.width() # 320
122+
```
123+
124+
#### `height()`
125+
Get display height in pixels.
126+
127+
**Returns:** int - Display height
128+
129+
**Example:**
130+
```python
131+
h = DisplayMetrics.height() # 240
132+
```
133+
134+
#### `dpi()`
135+
Get display DPI (dots per inch).
136+
137+
**Returns:** int - Display DPI
138+
139+
**Example:**
140+
```python
141+
dpi = DisplayMetrics.dpi() # 130
142+
```
143+
144+
#### `pct_of_width(pct)`
145+
Get percentage of display width.
146+
147+
**Parameters:**
148+
- `pct` (int): Percentage (0-100)
149+
150+
**Returns:** int - Pixel value
151+
152+
**Example:**
153+
```python
154+
half_width = DisplayMetrics.pct_of_width(50) # 160 (50% of 320)
155+
```
156+
157+
#### `pct_of_height(pct)`
158+
Get percentage of display height.
159+
160+
**Parameters:**
161+
- `pct` (int): Percentage (0-100)
162+
163+
**Returns:** int - Pixel value
164+
165+
**Example:**
166+
```python
167+
quarter_height = DisplayMetrics.pct_of_height(25) # 60 (25% of 240)
168+
```
169+
170+
#### `min_dimension()`
171+
Get minimum dimension (width or height).
172+
173+
**Returns:** int - Minimum of width and height
174+
175+
**Example:**
176+
```python
177+
min_dim = DisplayMetrics.min_dimension() # 240
178+
```
179+
180+
#### `max_dimension()`
181+
Get maximum dimension (width or height).
182+
183+
**Returns:** int - Maximum of width and height
184+
185+
**Example:**
186+
```python
187+
max_dim = DisplayMetrics.max_dimension() # 320
188+
```
189+
190+
#### `pointer_xy()`
191+
Get current pointer/touch coordinates.
192+
193+
**Returns:** tuple - (x, y) coordinates, or (-1, -1) if no active input device
194+
195+
**Example:**
196+
```python
197+
x, y = DisplayMetrics.pointer_xy()
198+
if x != -1:
199+
print(f"Touch at ({x}, {y})")
200+
```
201+
202+
## Practical Examples
203+
204+
### Responsive Layout
205+
206+
```python
207+
from mpos import Activity, DisplayMetrics
208+
import lvgl as lv
209+
210+
class MyApp(Activity):
211+
def onCreate(self):
212+
screen = lv.obj()
213+
214+
# Create a button that's 50% of display width
215+
button = lv.button(screen)
216+
button.set_width(DisplayMetrics.pct_of_width(50))
217+
button.set_height(DisplayMetrics.pct_of_height(20))
218+
button.center()
219+
220+
self.setContentView(screen)
221+
```
222+
223+
### Adaptive UI Based on Screen Size
224+
225+
```python
226+
from mpos import DisplayMetrics
227+
228+
def get_font_size():
229+
"""Return appropriate font size based on display size."""
230+
min_dim = DisplayMetrics.min_dimension()
231+
232+
if min_dim < 200:
233+
return lv.font_montserrat_12
234+
elif min_dim < 300:
235+
return lv.font_montserrat_16
236+
else:
237+
return lv.font_montserrat_20
238+
```
239+
240+
### Touch Event Handling
241+
242+
```python
243+
from mpos import DisplayMetrics
244+
import lvgl as lv
245+
246+
def handle_scroll(event):
247+
"""Handle scroll event with pointer coordinates."""
248+
x, y = DisplayMetrics.pointer_xy()
249+
250+
if x == -1:
251+
print("No active input device")
252+
return
253+
254+
# Check if touch is in specific region
255+
if x < DisplayMetrics.pct_of_width(50):
256+
print("Touch in left half")
257+
else:
258+
print("Touch in right half")
259+
```
260+
261+
## Implementation Details
262+
263+
### File Structure
264+
265+
```
266+
mpos/ui/
267+
├── display_metrics.py # Core DisplayMetrics class
268+
├── display.py # Initialization (init_rootscreen)
269+
└── __init__.py # Exports DisplayMetrics
270+
```
271+
272+
### Initialization Flow
273+
274+
1. **System Startup** - `init_rootscreen()` is called
275+
2. **Query Display** - Gets resolution and DPI from LVGL
276+
3. **Set Metrics** - Calls `DisplayMetrics.set_resolution()` and `DisplayMetrics.set_dpi()`
277+
4. **Ready to Use** - Apps can now call DisplayMetrics methods
278+
279+
```python
280+
# In display.py
281+
def init_rootscreen():
282+
screen = lv.screen_active()
283+
disp = screen.get_display()
284+
width = disp.get_horizontal_resolution()
285+
height = disp.get_vertical_resolution()
286+
dpi = disp.get_dpi()
287+
288+
# Initialize DisplayMetrics
289+
DisplayMetrics.set_resolution(width, height)
290+
DisplayMetrics.set_dpi(dpi)
291+
```
292+
293+
## Design Patterns
294+
295+
### Singleton Pattern
296+
297+
DisplayMetrics uses the singleton pattern with class variables and class methods:
298+
299+
```python
300+
class DisplayMetrics:
301+
_width = None # Shared across all "instances"
302+
303+
@classmethod
304+
def width(cls):
305+
return cls._width
306+
```
307+
308+
This avoids the need for instance creation while maintaining a single source of truth.
309+
310+
### Class Method Delegation
311+
312+
All methods are class methods, so no instance is needed:
313+
314+
```python
315+
# No need for DisplayMetrics() or DisplayMetrics.get()
316+
width = DisplayMetrics.width() # Direct class method call
317+
```
318+
319+
## Related Frameworks
320+
321+
- **[AudioFlinger](audioflinger.md)** - Audio management (similar singleton pattern)
322+
- **[ConnectivityManager](connectivity-manager.md)** - Network connectivity
323+
- **[SensorManager](sensor-manager.md)** - Sensor access
324+
325+
## See Also
326+
327+
- [Architecture Overview](../architecture/overview.md)
328+
- [Frameworks](../architecture/frameworks.md)
329+
- [Creating Apps](../apps/creating-apps.md)

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ nav:
5151
- Frameworks:
5252
- AudioFlinger: frameworks/audioflinger.md
5353
- ConnectivityManager: frameworks/connectivity-manager.md
54+
- DisplayMetrics: frameworks/display-metrics.md
5455
- DownloadManager: frameworks/download-manager.md
5556
- LightsManager: frameworks/lights-manager.md
5657
- Preferences: frameworks/preferences.md

0 commit comments

Comments
 (0)