Best place to go for new-user-type questions?
Closed this issue ยท 15 comments
Hiya,
I'm enjoying the library and making baby steps in writing my own gui app for the first time ever.
I think I'm making some basic user mistakes -- they're not bugs, I just don't know what I'm doing. Is there any sort of community space for library users to congregate and discuss such things?
Hi @chreekat! I hadn't thought about that. I'm looking into it, and Gitter seems to be a good solution (I noticed Streamly uses it).
Meanwhile, you can ask here! I want to create some sort of "Recipes" section in the docs, explaining how to do common things (with associated code snippets), and this may be a good way to kickstart it.
Hey! Gitter works, it's bridged to Matrix which is mainly what I use.
As for what i'm trying to do: I want a keybinding that toggles editing of a piece of text. By pressing 'e', I want to turn a label
into a textField
and ensure the textField is focused. Pressing 'Enter' should "save" the text and turn it back into a label.
What actually happens:
- The textField does not receive focus until I click on it.
- After I've clicked on it, I can successfully toggle back and forth between the label and textField with the Edit and Save bindings ('e' and 'Enter'). However, now there's a second problem: the 'e' is read as input to the textField!
I tried to prevent the second problem with ignoreChildrenEvts
, but I'm not sure that's even supposed to work. I might have it backwards -- do events bubble up?
The code in question: https://github.com/chreekat/orgr/blob/c023cb8ac35a0b00e3d602451c309133101243ef/orgr/src/Orgr/ProcessIn.hs
I have a hunch about the textField not receiving focus, by the way.
The way I've coded it, the label
and textField
are actually added or removed from the document model. Does that confuse the setFocus action? Do I need to include both of them and just toggle their visibility, rather than add and remove them? That's one of my newbie questions: When is right to add or remove elements from the view rather than just toggle visibility?
I don't know what's up with the second problem, though.
Thanks for the easy-to-test example! My answers are below.
Issue when setting focus
The setFocus
function internally relies on the widgetKeyMap
field from WidgetEnv
, which maps user-defined keys to nodes in the active widget tree. In the example you provided, when "e" is pressed, the "edit-box" nodeKey
cannot be found, causing the setFocus operation to fail; this happens because when calling setFocus
, the wenv
instance points to the same instance as when the event handler started (meaning the map is based on the previous version of the widget tree).
There are two options to make it work:
- Keep both items and toggle visibility. Since you expect a single widget in
thing
, you could wrap them in anhstack
/vstack
/zstack
(since a single element will be visible at any time, there is no difference).
buildUI :: WidgetEnv Model Event -> Model -> WidgetNode Model Event
buildUI _ _model@(Model is editing) =
let kmap = case editing of
NotEditing -> viewKeymap
Editing -> editKeymap
thing = hstack [
label (leItem (head is))
`nodeVisible` (editing == NotEditing),
textFieldV (leItem (head is)) UpdateItem
`nodeKey` "edit-box"
`nodeVisible` (editing == Editing)
]
in keystroke_
(fmap (fmap HandleUser) kmap)
-- FIXME: This doesn't do what I expect.
[ignoreChildrenEvts]
$ box_ [alignCenter, alignMiddle] thing
- Create a separate event for setting focus (something like
ActivateEdit
). In this case, instead of directly callingsetFocus
, you will returnEvent (HandleUser ActivateEdit)
. This event handler will, in turn, callsetFocus
. The difference is thatwenv
will now be bound to an instance that contains the updated widget tree, since thehandler
function will have been called again.
handler wenv _node model = \case
...
HandleUser x
| Save <- x ->
...
| Edit <- x ->
trace
"Edit"
[ Monomer.Model model{modelEditMode = Editing}
,
Event (HandleUser Activate)
]
| ActivateEdit <- x -> [setFocusOnKey wenv "edit-box"]
Issue with "e" being shown in the textField
This one is a bit confusing since keystroke
does what it should, but I think it can be extended/improved. The source of this issue is that there are two keyboard-related events:
- KeyAction: sent by SDL when a key is pressed/released.
- TextInput: sent by SDL when a key combination results in a valid text event. In general pressing letters result in letters, but this also allows receiving characters with accents and other language-dependent symbols (SDL takes care of all the processing).
The keystroke widget works based on KeyAction
and, when ignoreChildrenEvts
is set, child widgets will not receive the KeyAction
event if it was processed by keystroke
. The problem is that TextInput
will still be generated and, since it is processed after the handlers for KeyAction
run, the now visible textField will receive it.
I will think about how keystroke
could filter this event but, meanwhile, one option is using "C-e" instead of just "e". In general, combinations with Ctrl/Cmd do not result in valid text events, so you will not run into this issue.
A fantastic explanation -- thank you!
I fully understand the issue with setting focus now. :) I do have one followup feature request: can there be a verbose mode that prints some error to stderr when setFocus fails?
As for KeyAction versus TextInput: I think I'll use C-e for now, but in general I'm definitely interested in single-key bindings that can avoid this problem. :)
Thanks again!
I'm content to close this issue now if you want. Up to you
Nice!
I'll keep the issue open for a bit more since I'm looking into what we discussed. I'll keep you posted!
I just released 1.2.0.0. Besides a few other changes and additions, this version:
- Filters
TextInput
events if a single letter handler just matched onkeystroke
. - Deprecates
setFocusOnKey
(function) and addsSetFocusOnKey
(data constructor, only receiving the widget. key). With this change, Composite internally takes widget tree changes into account, avoiding the confusion caused by the previous approach.
I tested on your original version, and both scenarios worked fine (even when the widget did not previously exist). If you get a chance to try it and it works for you, feel free to close the issue.
Thanks!
You could also add Discussions tab here in the GitHub project - of course sometimes chat is easier. :-)
I haven't really used it much myself...
@juhp thanks! I'll take a look into it.
I personally prefer using issues rather than chat (I have not yet looked into Discussions) since it gives me more time to think and come up with an answer that may be useful to future readers. Having said that, maybe having a chat channel is still a good idea if other people are interested in chiming in.
I tested on your original version, and both scenarios worked fine
Haha cool! That's certainly one way to solve the "new-user type questions": make sure they don't need to be asked in the first place. ;) Nice work!
@chreekat I'll close the issue for the time being. If you feel something is missing, please re-open it or create a new one. Thanks!
Very tiny scroll thumb
I hope its okay, that I ask here ...
I have a problem with scroll
scroll_
etc.
In my application I create a vstack
with a list of Widgets I create similar to the todo or books example. But my list, based on a xml file I parse is sometimes very large, like 2000 - 3000 items. And sometimes the xml file is shorter hence only 100 - 200 items.
in the case of the very large lists, the thum in the scroll bar becomes very tiny. Unfortunately, I could not figure out, how to cope with this in the expected way - I think I should do this somehow with the config for scroll_ ... right? I mean I was able to configure the look of the scrollbar, width, color radius etc. but the thumbnail is in relation to the number of items aka the length of the list.
my "solution" was to simply copy the source for Monomer.Widgets.Containers.Scrolll
and manually make something like
scrollStatus
:: ScrollCfg s e
-> WidgetEnv s e
-> WidgetNode s e
-> ScrollState
-> Point
-> ScrollContext
scrollStatus config wenv node scrollState mousePos = ScrollContext{..} where
...
hThumbRect = Rect {
_rX = caLeft - hScrollRatio * dx,
_rY = caTop + hScrollTop + (barW - thumbW) / 2,
-- old _rW = hScrollRatio * vpWidth,
_rW = minimal $ hScrollRatio * vpWidth,
_rH = thumbW
}
vThumbRect = Rect {
_rX = caLeft + vScrollLeft + (barW - thumbW) / 2,
_rY = caTop - vScrollRatio * dy,
_rW = thumbW,
-- old _rH = vScrollRatio * vpHeight
_rH = minimal $ vScrollRatio * vpHeight
}
minimal x
| x < 50 = 50
| otherwise = x
It fixes my problem ... but obviously I would like to understand how to do this in the expected way. Also this has the problem, that when you scroll down to the bottom, that you have to move the thumb over the end of the scrollbar end because it simply appends a larger rect to the actual thumb.
So yeah if you find the time to answer, that would be great and if I have to ask some where else let me know
Hi @simmihugs!
That is an issue on the scroll component; there is currently not a config option for setting a minimum thumb size (it should somehow adapt automatically). Could you create a new issue with the same content as your comment, so we can track it separately from this one?
Thanks!
I created Config option for setting a minimum thumb size #61