-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Fade disabled button icon when resource is a bitmap (#6217) #6381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 2 commits
008f894
cf066e5
e558fb3
372597b
e344f19
5ffa72f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,11 @@ | ||
| package theme | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "image" | ||
| "image/color" | ||
| _ "image/jpeg" // register JPEG decoder so DisabledResource can desaturate JPEG icons | ||
| "image/png" | ||
|
|
||
| "fyne.io/fyne/v2" | ||
| "fyne.io/fyne/v2/internal/svg" | ||
|
|
@@ -838,9 +842,44 @@ | |
| return "disabled_" + unwrapResource(res.source).Name() | ||
| } | ||
|
|
||
| // Content returns the disabled style content of the correct resource for the current theme | ||
| // Content returns the disabled style content of the correct resource for the current theme. | ||
| // SVG resources are recolored with the theme's disabled color; bitmap resources (PNG, JPEG, ...) | ||
| // are desaturated to greyscale since they cannot be recolored. | ||
| func (res *DisabledResource) Content() []byte { | ||
| return colorizeLogError(unwrapResource(res.source).Content(), Color(ColorNameDisabled)) | ||
| src := unwrapResource(res.source) | ||
| content := src.Content() | ||
| if svg.IsResourceSVG(src) { | ||
| return colorizeLogError(content, Color(ColorNameDisabled)) | ||
| } | ||
| return desaturateLogError(content) | ||
| } | ||
|
|
||
| // desaturateLogError returns a PNG-encoded greyscale copy of the given image bytes, | ||
| // preserving the alpha channel. If decoding or encoding fails, the original bytes are | ||
| // returned so the caller can still render something. | ||
| func desaturateLogError(src []byte) []byte { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As an internal API I would think that returning the error instead of logging it makes more sense.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in e558fb3 β |
||
| img, _, err := image.Decode(bytes.NewReader(src)) | ||
| if err != nil { | ||
| fyne.LogError("Failed to decode bitmap for disabled state", err) | ||
| return src | ||
| } | ||
| bounds := img.Bounds() | ||
| gray := image.NewNRGBA(bounds) | ||
| for y := bounds.Min.Y; y < bounds.Max.Y; y++ { | ||
| for x := bounds.Min.X; x < bounds.Max.X; x++ { | ||
| // Convert via NRGBA so the luminance is computed on unpremultiplied | ||
| // channels β otherwise partially-transparent pixels go too dark. | ||
| n := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA) | ||
| lum := uint8((299*uint32(n.R) + 587*uint32(n.G) + 114*uint32(n.B)) / 1000) | ||
|
Check failure on line 873 in theme/icons.go
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are these magic numbers?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ITU-R BT.601 luma coefficients. Replaced with named constants ( |
||
| gray.SetNRGBA(x, y, color.NRGBA{R: lum, G: lum, B: lum, A: n.A}) | ||
| } | ||
| } | ||
| var buf bytes.Buffer | ||
| if err := png.Encode(&buf, gray); err != nil { | ||
| fyne.LogError("Failed to encode desaturated bitmap", err) | ||
| return src | ||
| } | ||
| return buf.Bytes() | ||
| } | ||
|
|
||
| // ThemeColorName returns the fyne.ThemeColorName that is used as foreground color. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -135,6 +135,25 @@ func TestButton_DisabledIconChangedDirectly(t *testing.T) { | |
| assert.Equal(t, render.icon.Resource.Name(), fmt.Sprintf("disabled_%v", searchBaseName)) | ||
| } | ||
|
|
||
| func TestButton_DisabledBitmapIcon(t *testing.T) { | ||
| pngIcon := fyne.NewStaticResource("fyne.png", iconData) | ||
| button := NewButtonWithIcon("Test", pngIcon, nil) | ||
| render := test.TempWidgetRenderer(t, button).(*buttonRenderer) | ||
|
|
||
| // While enabled the original bitmap resource is rendered untouched. | ||
| assert.Equal(t, pngIcon, render.icon.Resource) | ||
|
|
||
| // When disabled the resource is wrapped so DisabledResource.Content() | ||
| // returns a desaturated PNG copy β the icon is recolored, not faded. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This states the expected behaviour but does not test it. Please expand this test to confirm that pixels (at least some of them) have been desaturated.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 372597b β the test now decodes |
||
| button.Disable() | ||
| assert.True(t, strings.HasPrefix(render.icon.Resource.Name(), "disabled_"), | ||
| "disabled icon should be wrapped: %s", render.icon.Resource.Name()) | ||
|
|
||
| // Re-enabling restores the original resource reference. | ||
| button.Enable() | ||
| assert.Equal(t, pngIcon, render.icon.Resource) | ||
| } | ||
|
|
||
| func TestButton_Focus(t *testing.T) { | ||
| tapped := false | ||
| button := NewButton("Test", func() { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that "since they cannot be recolored" in the docs is required - focus on behaviour in public docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in e558fb3 β dropped the implementation reason, kept only the behaviour.