Skip to content
1 change: 1 addition & 0 deletions changes/4051.bugfix.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WinForms buttons with icons will now reliably scale to 32x32px icon size, regardless of the size of the original icon image.
1 change: 1 addition & 0 deletions changes/4051.bugfix.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`Screen.as_image` now properly accounts for HiDPI scaling on Winforms.
1 change: 1 addition & 0 deletions examples/button/button/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def startup(self):
button9 = toga.Button(
icon=toga.Icon("resources/star"),
)
print("WIDTH", button9._impl.native.Image.Width, button9._impl.scale_in(32))

# Add components for the second row of the outer box
inner_box2 = toga.Box(
Expand Down
Binary file modified examples/button/button/resources/star.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion testbed/tests/widgets/test_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ async def test_icon(widget, probe):
probe.assert_no_icon()
initial_height = probe.height

# Set an icon
# Set an icon. The icon image is bigger than 32x32, so it should be scaled
widget.icon = "resources/icons/red"
await probe.redraw("Button is now an icon button")

Expand Down
12 changes: 9 additions & 3 deletions winforms/src/toga_winforms/screens.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,17 @@ def get_size(self) -> Size:
return Size(self.scale_out(bounds.Width), self.scale_out(bounds.Height))

def get_image_data(self):
bitmap = Bitmap(*self.get_size())
bitmap = Bitmap(
self.scale_in(self.get_size()[0]), self.scale_in(self.get_size()[1])
)
graphics = Graphics.FromImage(bitmap)
source_point = Point(*self.get_origin())
source_point = Point(
self.scale_in(self.get_origin()[0]), self.scale_in(self.get_origin()[1])
)
destination_point = Point(0, 0)
copy_size = WinSize(*self.get_size())
copy_size = WinSize(
self.scale_in(self.get_size()[0]), self.scale_in(self.get_size()[1])
)
graphics.CopyFromScreen(source_point, destination_point, copy_size)
stream = MemoryStream()
bitmap.Save(stream, Imaging.ImageFormat.Png)
Expand Down
16 changes: 15 additions & 1 deletion winforms/src/toga_winforms/widgets/button.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from decimal import ROUND_UP

import System.Windows.Forms as WinForms
from System.Drawing import Bitmap, Size
from travertino.size import at_least

from toga.colors import TRANSPARENT
Expand Down Expand Up @@ -40,10 +41,23 @@ def get_icon(self):
def set_icon(self, icon):
self._icon = icon
if icon:
self.native.Image = icon._impl.native.ToBitmap()
self.native.Image = Bitmap(
icon._impl.bitmap,
Size(self.scale_in(32), self.scale_in(32)),
)
else:
self.native.Image = None

# Refreshing could change icon DPI, which means that the
# icon should be reassigned by the new scale.
def refresh(self):
super().refresh()
if self._icon:
self.native.Image = Bitmap(
self._icon._impl.bitmap,
Size(self.scale_in(32), self.scale_in(32)),
)

def set_background_color(self, color):
super().set_background_color(
self._default_background_color if color in {None, TRANSPARENT} else color
Expand Down
5 changes: 4 additions & 1 deletion winforms/tests_backend/widgets/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ def assert_no_icon(self):
def assert_icon_size(self):
icon = self.native.Image
if icon:
assert (icon.Size.Width, icon.Size.Height) == (32, 32)
assert (icon.Size.Width, icon.Size.Height) == (
self.impl.scale_in(32),
self.impl.scale_in(32),
)
else:
pytest.fail("Icon does not exist")
Loading