-
Notifications
You must be signed in to change notification settings - Fork 721
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
feat(ColorPicker): Add ColorPicker, ColorPickerSlider & ColorSpectrum #3955
feat(ColorPicker): Add ColorPicker, ColorPickerSlider & ColorSpectrum #3955
Conversation
Defined keys can now be used instead of strings for resource lookup the same as in WinUI.
Some strings contain invalid formatting that won't work in C#/.NET
Very impressive! And the thorough PR documentation is a great help. 👍 Your questions:
Not sure off the top of my head. Do you have an example?
PS: re the Codacy tool - for C++-converted code, take the warnings with a grain of salt; fidelity with the original code is preferable to following C# stylistic best practices. |
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.
The imported code looks good as best as I can judge - the thorough importation notes are much appreciated.
It looks like the build is failing on some (all?) platforms because Math.Clamp() isn't available in .NET Standard 2.0 - you can use the internal MathEx.Clamp()
method instead.
src/Uno.UI/Uno.UI.csproj
Outdated
@@ -153,6 +153,8 @@ | |||
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Folder Include="Microsoft\UI\Xaml\Controls\Common\" /> |
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.
Remove the changes to Uno.UI.csproj from the PR (and you can safely revert them locally too) - they get added when you include files via Visual Studio, but really they're unnecessary, because files are picked up by globbing.
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.
Yes, I noticed that when I imported the Strings folder it just popped up automatically in the solution. I'll be sure to clean this up!
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.
Concerning InvalidOperationException
, I debated using that one or
InvalidEnumArgumentException
. Both don't fit 100% so I went with the generic base Exception with a note to take another look. I'll be happy to switch it, thanks for the recommendation.
The string formatting methods can take arguments to automatically insert the specified arguments into the string. In C# this might look like "The value is {0:X2}" -> "The value is FF" in C++/WinRT the string formatting is different. Instead I have localized strings that look like: uno/src/Uno.UI/Microsoft/UI/Xaml/Controls/ColorPicker/Strings/en-us/Resources.resw Lines 216 to 219 in 938e886
Great! I was hoping you would say that. I ensured the code is 1-1 all places possible but where I had to re-write I commented out the C++ code nearby so you can compare in the future. Especially important when porting in future changes from WinUI to know where the code goes if it was replaced with a C# implementation. Codacy doesn't like that.
I was actually referring to the XAML files where I noticed in NumberBox all the Contract7Present namespace usage was commented out:
I'm feeling brave and will remove the clamping. I was hitting overflows in an earlier port but I think some fixes elsewhere will render these unnecessary now. It will then match the original C++ implementation as well. I have another question:
|
This code is now aligned more closely with C++ which does not use clamping. In the original C# port overflows were occurring so clamping was added. It is believed other fixes make the clamping unnecessary now.
I messed up the reply 😆. It shouldn't be necessary to comment it out with Uno 3.0, the ContractPresent/NotPresent markup should work correctly.
Let me address the broader question of events/lifetime concerns in Uno, then try to explain what's going on in Event handling patterns in Uno revolve around the fact that on iOS, in particular, it's very easy to create memory leaks because the underlying native runtime uses reference-counting for memory management. That means that any reference cycle - like a child view holding a hard reference to a parent view - automatically creates a leak where that subtree will never get collected. This most commonly manifests when subscribing to events on child views. The pattern to address this in Uno is typically the following:
That way the reference cycle is guaranteed to be broken when the view is removed the visual tree, avoiding a leak. It's not obligatory to use the 'disposable pattern' for this, you can subscribe+unsubscribe in a normal way if you prefer. The disposable pattern just makes it somewhat easier to avoid double subscriptions, forgotten unsubscriptions etc. As for how the 'disposable pattern' in NumberBox works: It's making use of two fairly simple types,
So to take a subsection of the relevant code: private void InitializeTemplate()
{
_eventSubscriptions.Disposable = null;
var registrations = new CompositeDisposable();
if (this.GetTemplateChild(c_numberBoxDownButtonName) is RepeatButton spinDown)
{
spinDown.Click += OnSpinDownClick;
registrations.Add(() => spinDown.Click -= OnSpinDownClick);
}
...
_eventSubscriptions.Disposable = registrations;
} What that code is doing is:
Sorry for the long reply, but I'm probably going to turn this comment into a section in the docs 😄 |
These changes are not needed as files/folders are automatically pulled in
These are the full, unmodified files from WinUI -- spaces included.
Removes most places where 'this.' was being used in code. In C++ it isn't used at all and was quite inconsistent in the C# port. Now 'this.' is only used for public properties and events.
I normally clean up the event handlers and have a note to get that done. However, looking at NumberBox I realized there might be more to it. Thanks so much for your detailed response! I was planning on unsubscribing one-by-one in the Unloaded event. However, using the reactive pattern is definitely helpful so you can remove all at once. It should simplify a lot of code and help minimize chances of mistakes. I'll take a look at what makes the most sense here. |
@davidjohnoliver I have been testing this with Android and the color spectrum is not rendering at all and the control is zero size. The other components of the ColorPicker seem to appear just fine though. As the ColorSpectrum was working fine in C# this is either a problem with the Uno Platform or a simple mistake importing the code into Uno. Either way, I can't seem to find any errors being reported to discover what the problem might be. I think I need someone with in-depth knowledge of Uno to take a look at what the problem might be. If you have a chance to look at this please see ColorPicker_SimpleText in the sample app. |
} | ||
|
||
// Uno Doc: C++ methodology does not easily convert to C# | ||
/*wchar_t *end; |
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.
Codacy found an issue: Remove this commented out code.
strongThis.UpdateBitmapSources(); | ||
strongThis.UpdateEllipse(); | ||
}); | ||
//}); |
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.
Codacy found an issue: Remove this commented out code.
// On RS2 and later, we put the spectrum images in a loaded image surface, | ||
// which we then put into a SpectrumBrush. | ||
// Uno Doc: SpectrumBrush is disabled for now | ||
//private LoadedImageSurface m_hueRedSurface; |
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.
Codacy found an issue: Remove this commented out code.
} | ||
}); | ||
|
||
//if (m_createImageBitmapAction != null) |
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.
Codacy found an issue: Remove this commented out code.
|
||
// If we're adding a small increment, then we'll just add or subtract 1. | ||
// If we're adding a large increment, then we want to snap to the next | ||
// or previous major value - for hue, this is every increment of 30; |
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.
Codacy found an issue: Remove this commented out code.
} | ||
|
||
// Uno Doc: The following code is not supported in C# and is removed. | ||
//byte *pixelBuffer = nullptr; |
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.
Codacy found an issue: Remove this commented out code.
{ | ||
for (int x = 0; x < width; x++) | ||
{ | ||
//if (workItem.Status == AsyncStatus.Canceled) |
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.
Codacy found an issue: Remove this commented out code.
private SolidColorBrush m_checkerColorBrush; | ||
|
||
// Uno Doc: Added to dispose event handlers | ||
private SerialDisposable _eventSubscriptions = new SerialDisposable(); |
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.
Codacy found an issue: Make '_eventSubscriptions' 'readonly'.
double ellipseY = Canvas.GetTop(selectionEllipsePanel) + selectionEllipsePanel.Height / 2; | ||
|
||
this.EllipseXTextBlock.Text = Math.Round(ellipseX).ToString(); | ||
this.EllipseYTextBlock.Text = Math.Round(ellipseY).ToString(); |
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.
Codacy found an issue: Define the locale to be used in this string operation.
private Vector4 m_oldHsvColor = new Vector4(0.0f, 0.0f, 1.0f, 1.0f); | ||
|
||
// Uno Doc: Added to dispose event handlers | ||
private SerialDisposable _eventSubscriptions = new SerialDisposable(); |
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.
Codacy found an issue: Make '_eventSubscriptions' 'readonly'.
var strongThis = this; | ||
//m_createImageBitmapAction.Completed = new AsyncActionCompletedHandler( | ||
//(IAsyncAction asyncInfo, AsyncStatus asyncStatus) => | ||
//{ |
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.
Codacy found an issue: Remove this commented out code.
this.BlueTextBlock.Text = this.ColorPicker.Color.B.ToString(); | ||
this.AlphaTextBlock.Text = this.ColorPicker.Color.A.ToString(); | ||
|
||
var verifySubclass = new MyColorSpectrum(); |
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.
Codacy found an issue: Remove the unused local variable 'verifySubclass'.
private Rectangle m_alphaSliderBackgroundRectangle; | ||
private ImageBrush m_alphaSliderCheckeredBackgroundImageBrush; | ||
|
||
private IAsyncAction m_alphaSliderCheckeredBackgroundBitmapAction = null; |
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.
Codacy found an issue: Make 'm_alphaSliderCheckeredBackgroundBitmapAction' 'readonly'.
public ColorPicker() | ||
{ | ||
// Uno Doc: Not supported | ||
//__RP_Marker_ClassById(RuntimeProfiler::ProfId_ColorPicker); |
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.
Codacy found an issue: Remove this commented out code.
string hexString = string.Format(CultureInfo.InvariantCulture, "#{0:X8}", hexValue); | ||
|
||
// We'll size this string to accommodate "#XXXXXXXX" - i.e., a full ARGB number with a # sign. | ||
//wchar_t hexString[10]; |
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.
Codacy found an issue: Remove this commented out code.
} | ||
|
||
// Uno Doc: This section assigning rgbValue/alphaValue was re-written in C# to avoid strange usage of functions in C++ | ||
//const bool isAlphaEnabled = IsAlphaEnabled(); |
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.
Codacy found an issue: Remove this commented out code.
string hexString = string.Format(CultureInfo.InvariantCulture, "#{0:X6}", hexValue); | ||
|
||
// We'll size this string to accommodate "#XXXXXX" - i.e., a full RGB number with a # sign. | ||
//wchar_t hexString[8]; |
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.
Codacy found an issue: Remove this commented out code.
private Rectangle m_previousColorRectangle; | ||
private ImageBrush m_colorPreviewRectangleCheckeredBackgroundImageBrush; | ||
|
||
private IAsyncAction m_createColorPreviewRectangleCheckeredBackgroundBitmapAction = null; |
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.
Codacy found an issue: Make 'm_createColorPreviewRectangleCheckeredBackgroundBitmapAction' 'readonly'.
// Implementation of the signum function, which returns the sign of a number while discarding its value. | ||
/*template <typename T> | ||
int sgn(T val) | ||
{ |
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.
Codacy found an issue: Remove this commented out code.
@davidjohnoliver I see you already made the internal change, thanks. I also updated to the latest WinUI version to get some important bug fixes. On my end I'm complete, so it's ready whenever the Uno Team is ready to merge. |
It's in! Thanks again @robloo for this great contribution. :) |
Awesome! Thanks so much for all your guidance and help. I really appreciate all that you and the Uno Team has accomplished - it is no small feat. It's also a huge time saver for me going forward with cross-platform development. |
Thanks for the contribution!! |
@robloo Hi. Would we expect this to be in the current release 3.4? I'm not seeing the ColorPicker control on Android or other platforms... |
Hi @MelbourneDeveloper, yes this should be in current 3.4. Both iOS and Android were tested to work (however slow) when the code was added. Have you added the WinUI library to your project? This control is only available under the new Microsoft.UI.Xaml namespaces NOT the Windows.UI.Xaml namespace of vanilla UWP. If thats not it, it might be a regression so please file a new issue. |
GitHub Issue (If applicable): #
Closes #3875
PR Type
What kind of change does this PR introduce?
What is the current behavior?
The Microsoft.UI.Xaml.Controls.ColorPicker and related primitives (ColorSpectrum, ColorPickerSlider) are not implemented.
What is the new behavior?
The following controls are implemented:
Microsoft.UI.Xaml.Controls.ColorPicker
Microsoft.UI.Xaml.Controls.Primitives.ColorPickerSlider
Microsoft.UI.Xaml.Controls.Primitives.ColorSpectrum
PR Checklist
Please check if your PR fulfills the following requirements:
Screenshots Compare Test Run
results.Other information
Submission containing materials of a third party: Microsoft, MIT License https://github.com/microsoft/microsoft-ui-xaml
Internal Issue (If applicable):
Notes
Architecture of the ColorPicker itself can be found here:
https://github.com/microsoft/microsoft-ui-xaml/blob/786129a37d36890d9d8ef1a61f945cc2f3be19a4/dev/ColorPicker/readme.md
Questions