Skip to content
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

Android DatePicker widget #1408

Merged
merged 12 commits into from
Feb 19, 2022
Merged

Android DatePicker widget #1408

merged 12 commits into from
Feb 19, 2022

Conversation

otaj
Copy link

@otaj otaj commented Jan 13, 2022

This PR implements DatePicker for Android, as mentioned in #1407 Please go over it whether this is a good direction in which this should be heading. If yes, I will implement TimePicker for Android in a similar fashion.

PR Checklist:

  • All new features have been tested
  • All new features have been documented
  • I have read the CONTRIBUTING.md file
  • I will abide by the code of conduct

Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR - this is a really good start.

The implementation looks great for what it does; the only nitpick is a couple of places where the implementation isn't consistent with other platforms.

I've updated the demo app to highlight these problems:

  1. Argument list to the changed_date handler
  2. More extensive testing of the min/max capability
  3. Making the example app a little more generic, covering both dates and times (although I've commented out times for now, so it doesn't break the Android case)

self.picker_impl._dialog = None
self.picker_impl.set_value(new_value)
if self.picker_impl.interface.on_change:
self.picker_impl.interface.on_change(self.picker_impl, new_value)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value shouldn't be passed as part of the handler.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, alright, that is of corrected now

@otaj
Copy link
Author

otaj commented Jan 15, 2022

Hi, sure, I will fix those. For some reason I thought that it makes sense to keep passing the value in the handler, but to be fair, it's much better to be consistent.

I will implement the TimePicker laster as well.

Couple things though, what do you think about keeping the values in DatePicker as instances of datetime.date and datetime.time respectively for TimePicker? Otherwise, it's gonna easily become awful to parse it and from POV of users, toga is a Python-first library.

Btw, one thing which would also help to highlight the required format for the users is to make the whole codebase typed - what do you think about that? I believe that is another piece of enhancement I could easily tackle.

@freakboy3742
Copy link
Member

Hi, sure, I will fix those. For some reason I thought that it makes sense to keep passing the value in the handler, but to be fair, it's much better to be consistent.

I will implement the TimePicker laster as well.

👍

Couple things though, what do you think about keeping the values in DatePicker as instances of datetime.date and datetime.time respectively for TimePicker? Otherwise, it's gonna easily become awful to parse it and from POV of users, toga is a Python-first library.

I completely agree with this. Using strings as the internal format is a decision oversight in the original patch - it makes a lot more sense for date/time to be the internal format, with string being allowed as an input if possible (parsed using ISO8601 format Y-M-D)

Btw, one thing which would also help to highlight the required format for the users is to make the whole codebase typed - what do you think about that? I believe that is another piece of enhancement I could easily tackle.

I agree that adding typing would be desirable as a form of implicit documentation. The only exception I can see to this would be when the typing is so broad that formally specifying it becomes more complicated than clarifying. This is something we saw in attempting to add typing to Django - there are some APIs where the types accepted are so broad that the type declaration becomes longer than the implementation of the method.

@otaj
Copy link
Author

otaj commented Jan 21, 2022

Hi, So TimePicker and DatePicker of Android are implemented. It is a bit more complex than it maybe needs to be, however, it could be easily extensible to other forms of pickers, such as colorpicker (with a bit of tweaking, granted)

Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general idea of a base class for "Pickers" is a really good one, but I've got some suggestions for improving the specific implementation here; comments inline.

I've also got concerns about whether the changes to the Toga core layer will break the existing implementations - we'll need to address those before merging. If you don't have access to the platforms that need to be updated, let me know and I can poke around.

docs/reference/data/widgets_by_platform.csv Show resolved Hide resolved

Test app for the Date Picker Example widget.

Date Picker is available only on Android and Win platforms
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This README is now out of date - that's my fault for not corrected it when I renamed the example, but it needs to be corrected before merging.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done now

@@ -0,0 +1,113 @@
from ..libs.android.view import OnClickListener, View__MeasureSpec
from ..libs.android.widget import EditText
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulling these utility classes into a standalone shared module is a good idea; however, convention inside Toga is to put these in a widgets/internal submodule, rather than using a 'protected' module name. See the Cocoa backend for an example of this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to internal

src/android/toga_android/widgets/_pickers.py Outdated Show resolved Hide resolved
_value_pack_fn = None
_extra_dialog_setters = None
_extra_dialog_args = None
_dialog_listener_class = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many of these method stub definitions are effectively reproducing what subclassing is for.

For example, rather than defining _to_obj_converter_class and _value_pack_fn, we could define a method on the PickerBase to_obj() that is given the args, and does the unpacking.

We can then declare a stub implementation in the base class that raises NotImplemented(), which is a more pythonic approach to ensuring subclasses implement all the required methods.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made everything as methods (mostly "private" with a single underscore), so this is removed. On top of that, the methods are defined as abstract methods, so they will throw an error if not overridden. I personally am not a big fan of this way of implementing, since most of these just return a single, static value, but here you go :)

@otaj
Copy link
Author

otaj commented Jan 31, 2022

Hi, so I worked in the requested changes. I've also reverted the changes to toga core - the only other platform in which Pickers are implemented is Windows and I don't have an access to it to test it. My Android implementation is able to live with strings (converting everything behind the scenes to the appropriate objects), so it's not necessary for the changes to the core to be present.

@otaj
Copy link
Author

otaj commented Feb 18, 2022

Hi @freakboy3742 , is there anything more needed from me? Thanks!

Copy link
Member

@freakboy3742 freakboy3742 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the delayed review; I've been busy lately.

This looks great - some aspects are a little over-engineered by trying to be a little bit too abstract, but it's close enough that I'm happy to get it the rest of the way.

While I'm at it, I'll try to restore the "date/time as native format" piece.

Thanks for the contribution - it's much appreciated!

@codecov
Copy link

codecov bot commented Feb 19, 2022

Codecov Report

Merging #1408 (62fc54c) into master (090370a) will increase coverage by 0.04%.
The diff coverage is 100.00%.

Impacted Files Coverage Δ
src/core/toga/widgets/datepicker.py 100.00% <100.00%> (ø)
src/core/toga/widgets/timepicker.py 100.00% <100.00%> (ø)
src/core/toga/app.py 85.92% <0.00%> (ø)

@freakboy3742 freakboy3742 merged commit eb6884b into beeware:master Feb 19, 2022
@otaj
Copy link
Author

otaj commented Mar 2, 2022

Thank you, you pushed it quite far to the finish line! Happy to help, let me know if there is any other issue I can tackle, it was fun :)

@freakboy3742
Copy link
Member

@otaj Oh, there's always more issues to tackle :-)

On the simple end, any widget missing from the platform compatibility table is up for grabs. Some will be trivial (e.g. Divider); some have partial implementations (e.g., Canvas); and some will require some very deep thought, as well as consideration for what "platform native" means (e.g., Tree).

There are also some bugs on widgets in Android that may require a radical re-think of how widgets are implemented. For example, we have a Table widget for Android, but #1392 points out that the implementation won't work if there's a lot of data in the table.

Then there are the other platforms to consider.

In terms of "bigger picture" issues - #1436 is one of the biggest at the moment, but at the moment, it's at the level of "research project". I don't know what might be a viable solution here - if you want to do some research on what might be possible, that would be incredibly helpful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants