Skip to content

Commit

Permalink
Merge pull request #1969 from freakboy3742/audit-scrollcontainer
Browse files Browse the repository at this point in the history
[widget Audit] toga.ScrollContainer
  • Loading branch information
mhsmith authored Jul 12, 2023
2 parents d130fcb + 0e1302a commit f7dc1c8
Show file tree
Hide file tree
Showing 64 changed files with 2,690 additions and 1,252 deletions.
54 changes: 54 additions & 0 deletions android/src/toga_android/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from .libs.android.widget import RelativeLayout, RelativeLayout__LayoutParams


class Container:
def init_container(self, native_parent):
self.width = self.height = 0

context = native_parent.getContext()
self.native_content = RelativeLayout(context)
native_parent.addView(self.native_content)

self.dpi = context.getResources().getDisplayMetrics().densityDpi
# Toga needs to know how the current DPI compares to the platform default,
# which is 160: https://developer.android.com/training/multiscreen/screendensities
self.baseline_dpi = 160
self.scale = self.dpi / self.baseline_dpi

def set_content(self, widget):
self.clear_content()
if widget:
widget.container = self

def clear_content(self):
if self.interface.content:
self.interface.content._impl.container = None

def resize_content(self, width, height):
if (self.width, self.height) != (width, height):
self.width, self.height = (width, height)
if self.interface.content:
self.interface.content.refresh()

def refreshed(self):
# We must use the correct LayoutParams class, but we don't know what that class
# is, so reuse the existing object. Calling the constructor of type(lp) is also
# an option, but would probably be less safe because a subclass might change the
# meaning of the (int, int) constructor.
lp = self.native_content.getLayoutParams()
layout = self.interface.content.layout
lp.width = max(self.width, layout.width)
lp.height = max(self.height, layout.height)
self.native_content.setLayoutParams(lp)

def add_content(self, widget):
self.native_content.addView(widget.native)

def remove_content(self, widget):
self.native_content.removeView(widget.native)

def set_content_bounds(self, widget, x, y, width, height):
lp = RelativeLayout__LayoutParams(width, height)
lp.topMargin = y
lp.leftMargin = x
widget.native.setLayoutParams(lp)
1 change: 1 addition & 0 deletions android/src/toga_android/libs/android/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
ViewGroup__LayoutParams = JavaClass("android/view/ViewGroup$LayoutParams")
View__MeasureSpec = JavaClass("android/view/View$MeasureSpec")
View__OnFocusChangeListener = JavaInterface("android/view/View$OnFocusChangeListener")
View__OnScrollChangeListener = JavaInterface("android/view/View$OnScrollChangeListener")
View__OnTouchListener = JavaInterface("android/view/View$OnTouchListener")
ViewTreeObserver__OnGlobalLayoutListener = JavaInterface(
"android/view/ViewTreeObserver$OnGlobalLayoutListener"
Expand Down
42 changes: 20 additions & 22 deletions android/src/toga_android/widgets/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def __init__(self, interface):
self.interface = interface
self.interface._impl = self
self._container = None
self.viewport = None
self.native = None
self._native_activity = _get_activity()
self.create()
Expand All @@ -65,25 +64,30 @@ def container(self):

@container.setter
def container(self, container):
if self.container:
assert container is None, "Widget already has a container"

# container is set to None, removing self from the container.native
self._container.native.removeView(self.native)
self._container.native.invalidate()
self._container = None
elif container:
self._container = container
# When initially setting the container and adding widgets to the container,
# we provide no `LayoutParams`. Those are promptly added when Toga
# calls `widget.rehint()` and `widget.set_bounds()`.
self._container.native.addView(self.native)
if self._container:
self._container.remove_content(self)

self._container = container
if container:
container.add_content(self)

for child in self.interface.children:
child._impl.container = container

self.rehint()

@property
def viewport(self):
return self._container

# Convert CSS pixels to native pixels
def scale_in(self, value):
return int(round(value * self.container.scale))

# Convert native pixels to CSS pixels
def scale_out(self, value):
return int(round(value / self.container.scale))

def get_enabled(self):
return self.native.isEnabled()

Expand All @@ -103,9 +107,7 @@ def set_tab_index(self, tab_index):
# APPLICATOR

def set_bounds(self, x, y, width, height):
if self.container:
# Ask the container widget to set our bounds.
self.container.set_child_bounds(self, x, y, width, height)
self.container.set_content_bounds(self, x, y, width, height)

def set_hidden(self, hidden):
if hidden:
Expand Down Expand Up @@ -160,11 +162,7 @@ def set_color(self, color):
# INTERFACE

def add_child(self, child):
if self.viewport:
# we are the top level widget
child.container = self
else:
child.container = self.container
child.container = self.container

def insert_child(self, index, child):
self.add_child(child)
Expand Down
12 changes: 1 addition & 11 deletions android/src/toga_android/widgets/box.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
from travertino.size import at_least

from ..libs.activity import MainActivity
from ..libs.android.widget import RelativeLayout, RelativeLayout__LayoutParams
from ..libs.android.widget import RelativeLayout
from .base import Widget


class Box(Widget):
def create(self):
self.native = RelativeLayout(MainActivity.singletonThis)

def set_child_bounds(self, widget, x, y, width, height):
# We assume `widget.native` has already been added to this `RelativeLayout`.
#
# We use `topMargin` and `leftMargin` to perform absolute layout. Not very
# relative, but that's how we do it.
layout_params = RelativeLayout__LayoutParams(width, height)
layout_params.topMargin = y
layout_params.leftMargin = x
self.native.updateViewLayout(widget.native, layout_params)

def set_background_color(self, value):
self.set_background_simple(value)

Expand Down
64 changes: 29 additions & 35 deletions android/src/toga_android/widgets/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,33 +64,29 @@ def closed_path(self, x, y, path, *args, **kwargs):
path.close()

def move_to(self, x, y, path, *args, **kwargs):
path.moveTo(
self.container.viewport.scale * x, self.container.viewport.scale * y
)
path.moveTo(self.container.scale * x, self.container.scale * y)

def line_to(self, x, y, path, *args, **kwargs):
path.lineTo(
self.container.viewport.scale * x, self.container.viewport.scale * y
)
path.lineTo(self.container.scale * x, self.container.scale * y)

# Basic shapes

def bezier_curve_to(self, cp1x, cp1y, cp2x, cp2y, x, y, path, *args, **kwargs):
path.cubicTo(
cp1x * self.container.viewport.scale,
cp1y * self.container.viewport.scale,
cp2x * self.container.viewport.scale,
cp2y * self.container.viewport.scale,
x * self.container.viewport.scale,
y * self.container.viewport.scale,
cp1x * self.container.scale,
cp1y * self.container.scale,
cp2x * self.container.scale,
cp2y * self.container.scale,
x * self.container.scale,
y * self.container.scale,
)

def quadratic_curve_to(self, cpx, cpy, x, y, path, *args, **kwargs):
path.quadTo(
cpx * self.container.viewport.scale,
cpy * self.container.viewport.scale,
x * self.container.viewport.scale,
y * self.container.viewport.scale,
cpx * self.container.scale,
cpy * self.container.scale,
x * self.container.scale,
y * self.container.scale,
)

def arc(
Expand All @@ -100,10 +96,10 @@ def arc(
if anticlockwise:
sweep_angle -= math.radians(360)
path.arcTo(
self.container.viewport.scale * (x - radius),
self.container.viewport.scale * (y - radius),
self.container.viewport.scale * (x + radius),
self.container.viewport.scale * (y + radius),
self.container.scale * (x - radius),
self.container.scale * (y - radius),
self.container.scale * (x + radius),
self.container.scale * (y + radius),
math.degrees(startangle),
math.degrees(sweep_angle),
False,
Expand All @@ -128,28 +124,28 @@ def ellipse(
sweep_angle -= math.radians(360)
ellipse_path = Path()
ellipse_path.addArc(
self.container.viewport.scale * (x - radiusx),
self.container.viewport.scale * (y - radiusy),
self.container.viewport.scale * (x + radiusx),
self.container.viewport.scale * (y + radiusy),
self.container.scale * (x - radiusx),
self.container.scale * (y - radiusy),
self.container.scale * (x + radiusx),
self.container.scale * (y + radiusy),
math.degrees(startangle),
math.degrees(sweep_angle),
)
rotation_matrix = Matrix()
rotation_matrix.postRotate(
math.degrees(rotation),
self.container.viewport.scale * x,
self.container.viewport.scale * y,
self.container.scale * x,
self.container.scale * y,
)
ellipse_path.transform(rotation_matrix)
path.addPath(ellipse_path)

def rect(self, x, y, width, height, path, *args, **kwargs):
path.addRect(
self.container.viewport.scale * x,
self.container.viewport.scale * y,
self.container.viewport.scale * (x + width),
self.container.viewport.scale * (y + height),
self.container.scale * x,
self.container.scale * y,
self.container.scale * (x + width),
self.container.scale * (y + height),
Path__Direction.CW,
)

Expand All @@ -171,7 +167,7 @@ def fill(self, color, fill_rule, preserve, path, canvas, *args, **kwargs):
def stroke(self, color, line_width, line_dash, path, canvas, *args, **kwargs):
draw_paint = Paint()
draw_paint.setAntiAlias(True)
draw_paint.setStrokeWidth(self.container.viewport.scale * line_width)
draw_paint.setStrokeWidth(self.container.scale * line_width)
draw_paint.setStyle(Paint__Style.STROKE)
if color is None:
a, r, g, b = 255, 0, 0, 0
Expand All @@ -180,7 +176,7 @@ def stroke(self, color, line_width, line_dash, path, canvas, *args, **kwargs):
if line_dash is not None:
draw_paint.setPathEffect(
DashPathEffect(
[(self.container.viewport.scale * float(d)) for d in line_dash], 0.0
[(self.container.scale * float(d)) for d in line_dash], 0.0
)
)
draw_paint.setARGB(a, r, g, b)
Expand All @@ -197,9 +193,7 @@ def scale(self, sx, sy, canvas, *args, **kwargs):
canvas.scale(float(sx), float(sy))

def translate(self, tx, ty, canvas, *args, **kwargs):
canvas.translate(
self.container.viewport.scale * tx, self.container.viewport.scale * ty
)
canvas.translate(self.container.scale * tx, self.container.scale * ty)

def reset_transform(self, canvas, *args, **kwargs):
canvas.restore()
Expand Down
Loading

0 comments on commit f7dc1c8

Please sign in to comment.