I have been following this guide and have the working example till the very end of the demo (very insightful tutorial, 10/10 would recommend):
https://codenameone.teachable.com/courses/java-for-mobile-devices-introducing-codename-one/lectures/2647773
Now I wanted to expand this to a more practical todo list by adding a Floating Action Button to add new items, like that:
fab = FloatingActionButton.createFAB(FontImage.MATERIAL_ADD);
fab.bindFabToContainer(current.getContentPane(), Component.RIGHT,
Component.BOTTOM);
fab.addActionListener(e -> { // show dialog for adding new item });
This by itself works fine. Now comes the tricky part. When using a long press event on any of the items, two things should happen:
the visual of the long pressed item should change to indicate it is being selected (not in terms of a Checkbox or ToggleButton "select" but a highlight.
the FAB should change its icon and actionlistener to delete the highlighted item.
The long press event is achieved in overriding the longPointPress method from the Checkbox class:
#Override
public void longPointerPress(int x, int y){
mainForm.longPressEvent(this);
// no event parameter for e.consume();
}
To my questions:
Q1: When I use the action listener from the ToggleButton, both the "normal" click event as well as the long Press event are fired. I need to distinguish between the two though.
The longPointerPress method does not have the event in the param list, hence I cannot consume the event after being done with my long Press Event activity. How can I prevent the normal action listener from firing?
Q2: For the "highlight effect", I would like the item to have a margin to all sides, overall shrinking the element by that amount. In other words, without increasing the previous total size. By just adding a margin though, the item gets bigger.
How could a shrink a given element with a margin to all sides, but preserve the original size?
Q3: A FAB only has the option to "setIcon", not "setMaterialIcon". Hence, I am currently recreating the FAB every time it changes, as I dont want to hustle with the involved styles. Is there a better way than this?
//this is the unwanted function, as I dont want to set the style myself
fab.setIcon(FontImage.createMaterial(icon, s));
//Delete FAB pressed, change to Add FAB
fab.remove();
fab = FloatingActionButton.createFAB(FontImage.MATERIAL_ADD);
fab.bindFabToContainer(current.getContentPane(), Component.RIGHT, Component.BOTTOM);
Action event is always invoked on pointer release regardless of whether there was a long press event fired as we don't "know" you processed the longPress event. You would need to create a flag such as:
private boolean handledInLongPress;
public void longPress(int x, int y) {
// do your stuff
handledInLongPress = true;
}
private void handleActionEvent(ActionEvent ev) {
// I'm using this as a placeholder for your event code
// block the event from propagating and undo anything it might
// have triggered
ev.consume();
}
I would recommend using setUIID() on the elements and define a "delete*" set of UIID's. You can define smaller padding and fonts to create the effect of shrinking but that might be tricky if you have icons here as well. You can scale down said icons and keep the original for restoring in a client property.
FAB makes a lot of assumptions so re-creating it (or using two instances) is probably a better approach than trying to set the icon. There is no way to change the icon of the FAB at runtime in the current implementation.
Related
I am designing a GUI using C, Glade, and Gtk.
I have some signals configured in glade to update the labels of various widgets, mainly GtkButton and GtkLabel. The overall functionality is that when a certain radio button is clicked, all button and labels change in response (language selection).
I am using the function gtk_label_set_label(...) in the widgets _draw() function and it works as expected (text changes, g_print occurs (once)).
gboolean on_lblMyLabel_draw(GtkLabel *label, gpointer *user_data) {
gtk_label_set_label(label, "custom text");
g_print("%s\n", "custom text");
return FALSE;
}
However, when I attempt the same from a button,
gboolean on_btnMyButton_draw(GtkButton *button, gpointer *user_data) {
gtk_button_set_label(button, "custom text");
g_print("%s\n", "custom text");
return FALSE;
}
The text does not update, but dissappears, and the g_print() statement prints forever (as if the draw is recursively calling itself).
Funnily, if I move the button code from _draw to _click, it works as expected, however, I need the GUI to redraw itself, so updating on click is impractical.
Is there a way, using _draw() to prevent this?
Is there a better way to do this?
thx!
Is there a way, using _draw() to prevent this?
No, and you shouldn’t be using the draw signal for this either. It has an entirely different purpose, and will be called each time a widgets redraws itself. That’s also the reason why your button is going into an infinite recursion: you changed its label so it figures it needs to be redrawn; that redraw leads to your callback being called, which again changes the label, etc etc
Is there a better way to do this?
Yes, and you mention it yourself already: make sure you do the logic of changing the widgets in the appropriate place (for example, on a click event), and let the GTK widgets take care of redrawing themselves.
Unless you’re doing something very exotic (like not running an event loop, which you automatically get with GtkApplication), this will all work fine.
I need to implement a behavior:
when element clicked - one thing happens
but when it's clicked and held for more than one second, something else happens (e.g element becomes draggable) and then the first event never fires
I think I know how to catch click&hold type of events, but how to distinguish between first and second?
Can you show me how to do that using this jsbin. I already made the "click, hold & drag" part, except that it is still firing the 'click' event after dragging the element and it shouldn't.
again: element clicked - one event, click and hold - element is draggable (even after mouse up) and when clicked again it's back to normal (undraggable) state.
I am not looking for a trivial solution, it has to be built using Rx.Observable or at least Bacon's streamEvent object
Thank you
I think you were pretty close with your solution, but probably it is not possible to elegantly achieve what you want while using the browser's built-in click event.
HERE is my attempt to tackle your problem.
The main idea is to define your own click streams like so:
var clicks = downs.flatMapLatest(function(){
return ups.takeUntil(Rx.Observable.timer(250));
});
var longDownsStart = downs.flatMapLatest(function(){
return Rx.Observable.timer(1000).takeUntil(ups);
});
In case of clicks we wait max 250 ms after a mouse down for a mouse-up; in case of the latter we generate the event only if there was no mouse-up within 1000 ms.
There might be some corner cases in which the code does not work as intended.
Here is my proposed solution (with Bacon.js).
My question is simple: WHEN (on what event?) can I be sure that a control has fully loaded and has its states and templates also?
Why am I asking:
I'm trying to restore the state of my own WP7 control after recovering from tombstone. This control looks like a calendar in a weekly view. In this calendar you can select many items displayed as colored Rectangles.
If I select any of them, and then go to tombstone and come back to the page, it seems like my control forgot which Rectangles were selected. In fact, it did NOT forget the data itself, but the Rectangles forgot their selected state.
After recovering from tombstone, I try to select the Rectangles by setting their VisualState to "Selected" (which works in any other scenario). I found out, that it fails, because VisualStateManager can't find the "Selected" state.
I know this is tricky, because when coming back from tombstone the controls do not build exactly as in any "normal" case. (for example Bindings and Templates do not apply in the same order) But up until now I could always trust, that when FrameworkElement.Loaded fired, I had my controls ready. Now it seems like VisualState is not. (I tried to set the state from Loaded event handler, but results are the same, VisualStateManager.GoToState returns with false.)
What more can I do?
This is a tricky one! I have also experienced issues where UI events fire before the UI itself is fully constructed, see this blog post for an example. My general approach to this is to handle the LayoutUpdated event, which fires each time the visual tree is updated. You will find that this event fires multiple times, both before and after the Loaded event.
When the Layoutupdated event fires, you can check whether the visual state change has worked, if so, no longer handle the event. If not, keep trying!
Within your loaded event, try the following:
// try to set the state
if (VisualStateManager.GoToState(myControl, "myState") == false)
{
// if failed, wait for the next LayoutUpdated event
EventHandler updateHandler = null;
updateHandler = (s, e2) =>
{
if (VisualStateManager.GoToState(myControl, "myState") == false)
{
myControl.LayoutUpdated -= updateHandler;
}
};
myControl.LayoutUpdated += updateHandler;
}
I'm having trouble implementing a dynamic pivot control.
What I would like to do, is create an image gallery using the pivot, where you could change the image by swiping.
This works natively with the pivot control by binding it's itemsource on my observable collection to display one image on each pivotitem.
The thing is that this solution takes a lot of memory when my gallery contains more than 10 pictures, as it creates an equal number of pivotitems.
What I tried is initializing my collection with 3 items, the currently displayed one, the previous and the next one, and when the user swipes, I updated my list.
I tried this using the "SelectionChanged" event and also with the gesture listener.. without success so far.
Has anyone ever tried to do this? And if yes, how can I implement it?
I do something similar, but don't use a Pivot control. Download the Silverlight toolkit (http://silverlight.codeplex.com/) and use a GestureListener to catch the swipe action:
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener DragCompleted="OnDrag"/>
</toolkit:GestureService.GestureListener>
Then in code, you have an event handler that catches the swipe event, and updates the image accordingly. Here is the event handler that I use to change my page:
private void OnDrag(object sender, DragCompletedGestureEventArgs e)
{
if (e.Direction == System.Windows.Controls.Orientation.Horizontal)
{
if (e.HorizontalChange < 0)
{
TurnPagesForward(1);
}
else
{
TurnPagesBackward(1);
}
}
}
If you update the items in your list your binding will be lost, so this will probably not work very well.
What might work is the following (but I have not tested it):
Start with an initial collection with five items.
Make sure you display the third item in the list so that the visible item is in the middle of the list.
When changing the visible page the SelectionChanged event is raised.
If you move to the right the selected index is changed to the fourth element. In this case you remove the first element in the list and add a new fifth element.
If you move to the left you do the opposite and remove the last element in the list.
This should ensure that you always can move back and forth while not removing or adding any visible items.
I am not sure if this will work, but it is worth a try. :-)
Don't do this. Lots of people have tried and it doesn't scale to large numbers of images.
Although it would be really nice if it was possible to use the pivot control to create a simple image navigation control it just isn't practical. You'll need to go the more complicated route of doing this yourself with a different method. (Lots of people have tried different ways. Go with what's best for your needs/experience/preferences.)
The answer to this question may turn out to be, "Don't use typed DataSets without using the Binding Navigator."
I am curious, however, about the behavior I'm seeing.
So, I created a form where every control was dragged from the data sources explorer.
I deleted the Binding Navigator because it is ugly and inappropriate for this particular form.
I added a ListBox and set the DataSource to the BindingSource.
Notice that the ListBox is not bound, it is just filling itself from the BindingSource.
By some magic that I wasn't counting on, moving around in the ListBox is navigating the BindingSource and all other controls are updating accordingly.
I can make changes to the bound controls and explicitly call EndEdit on the BindingSource and then update the DataSource through the Table Adapter. Works great.
When I make changes in the bound controls and click a new option in the ListBox, I want to be able to check for changes and prompt to save or reset if there are any.
Here is the strange part that I haven't been able to figure out.
No matter what event I attach to, DataSet.HasChanges doesn't return true until the second ListBox change.
I've searched and tried dozens of suggestions, most of them ridiculous, but a few that seemed promising.
No luck.
Edit: It isn't the second click that is significant, it is when you click back on the original (edited) item.
Since asking the question, I've learned a bit more about BindingSources, DataSets and TableAdapters.
Here is what works:
private void MyListBox_Click(object sender, EventArgs e)
{
this.myBindingSource.EndEdit();
if (myDataSet.HasChanges())
{
if (MessageBox.Show("Save changes?", "Before moving on", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
myTableAdapter.Update(myDataSet.myDataTable);
}
else
{
myDataSet.RejectChanges();
}
}
}