-
Notifications
You must be signed in to change notification settings - Fork 741
askMulti multiple question dialog support #4671
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
Conversation
I think the NoUI build failure is spurious/unrelated -- I tried it on my box and it works OK. |
This pull request introduces 6 alerts when merging ef3d493 into 8adce5f - view on LGTM.com new alerts:
|
The CI build is fixed in #4669, it comes from github bumping the image to Ubuntu 20.04 from 18.04 |
@jtanx That didn't quite seem to do it. NoUI build still failing for seemingly PR-unrelated reasons. |
It's still the same thing, you can see the gettext error:
Seems like GHA doesn't update to the latest HEAD when you rerun:
8adce5f is the commit before my merge. Try close and reopen the PR? Or rebase to master. |
Rebasing seems to have worked, thanks! |
This pull request introduces 6 alerts when merging c0da058 into 25c3b74 - view on LGTM.com new alerts:
|
BTW @jtanx I eventually landed on moving the children of a GScroll1Box within the nested window because I didn't come up with a better solution, possibly out of ignorance. It seems like one should be able to pick the displayed region of a window, but maybe not. If there's a more standard or just better way of doing that let me know. |
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'm on the fence for this one. This is getting to the point of being more a way to generate a complex modal / define a UI with gdraw rather than a pure 'ask' dialog you'd normally associate with what standard UI toolkits provide.
If you want to extend it, then I guess I won't stop you, but definitely for myself, I won't be doing any work towards extending gdraw, if anything I'd be trying to cut it back, like in #4664.
I do worry though, how much more difficult this will make future porting efforts, but I guess that too is a very long term goal, if at all possible.
Yes, that's specifically the point. It's intended to handle 80% of requests like #3663. (This has been requested by others too -- I just can't turn up the links at the moment.)
So GDK is a dead-end, GTK-4 is not currently being pursued because its too different (personal communication), but we might port to Qt someday (because that wouldn't be too different?) and therefore enhancement of UI functionality should stop until then? Do we have a year for that? A decade? Your UI porting work is more than admirable, it's beyond the call, really. But the whole project shouldn't just revolve around it. We desperately need to shift a large hunk of the extension burden off of C and the main dev folks here onto Python and other users. This feature is needed to do that. |
Like yeah, for sure that's why I'm saying I won't get in the way of gdraw changes if someone wants to do them. But at the same time I just want to make sure that (a.) there is a genuine use case (which you've highlighted), and (b.) we aren't digging the hole deeper for ourselves unnecessarily. For GFlowBox/GScroll1Box, can you go into further detail about what they solve/what you can't do with the existing controls? |
Sorry, I realize you mostly made the same point I'm making. There are some users who may be happy to design and raise their own I also suspect you underestimate how "custom" and strange the rest of the UI is and therefore how much work it would/will be to swap everything to a new widget set. In my view whatever additional work this involves will be dwarfed by the general problem. |
There's one part of this feature yet to implement that there are little pieces of, which is an optional apply callback and "protocol" that lets you make a dialog work the way the Transform dialog does. (That's what (One of the general layout reasons has to do with localization: the window designer doesn't necessarily know how long the individual strings will be so I wanted to remove the need to think about that as much as possible.) That's more or less what GFlowBox is for. Most widget sets have something like it. You narrow the container and the contents reflow, potentially with justification. If there's extra room the contents may spread out. (The relevant contents in this case will be radio buttons and checkboxes -- those are the only things that can spread out. I also provide the list-like widget used in askChoices() as an alternative but I find it to be kind of crappy.) Before these new boxes the only real general layout box is GHVBox, and it a) insists that the desired size of its contents should be the minimum size and b) doesn't have integrated scrolling. That means you can resize bigger but not really smaller -- the window just truncates. So I needed a container to put GFlowBoxes into in that could be genuinely resized and would scroll when needed. Frankly I was shocked that I had to write GScroll1Box: I thought there would already be some kind of scrolling container in gdraw. But it turned out that while there was (clearly) already a scrollbar widget there was no generic scrolling container: every use of a scroll bar is specific. The GList fakes its container aspect with how it paints. So does GMatrixEdit, although it works quite differently. Anyway, that's why there's GScroll1Box. It's filling an implementation hole. |
FWIW I don't think we should invest much time in improving gdraw either, and when we do it should be in the direction of what other widget sets are likely to already have (within reason). But there are two things I really wish it had.
Issue 2 raises a really silly situation: There's all this custom code to draw boxes of infinite variety around widgets, as evidenced in the X Resource Editor, and there's already a |
k = qspec->tag; | ||
assert( !PyDict_Contains(r, k) ); | ||
// XXX seems like v should have an extra reference at this point, | ||
// (because _SetItem() doesn't steal) but DECREFing v causes crashes |
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 entirely understand what this means, but are you sure you don't need to increment the reference count on Py_None
?
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.
That had been my impression but I guess that impression was wrong. I'll add the Py_None reference counting.
PyTuple_SetItem()
is documented as "stealing" a reference, hence the reference increment before the call. PyDict_SetItem() is documented as not stealing a reference: https://docs.python.org/3/c-api/dict.html#c.PyDict_SetItem , which in my mind means it should take a reference, which means that I should be decrementing my own reference to it after the call, but that causes crashes. So there's something I'm missing.
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.
Ref count should be fixed
fontforgeexe/multidialog.c
Outdated
} else { | ||
return false; | ||
} | ||
return true; |
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 understand. Why can't we just drop the else
and have a return false
line?
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'm just copying a common semantic from other handlers.
Looking through the code it I can't find an easy example of an event handler return value being attended to, so maybe it's irrelevant? I have been assuming that true
"consumes" the event and false
means the processing might continue to higher or lower handlers.
Can @jtanx offer any clarification?
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.
It's used e.g. here
Line 356 in e873b8f
handled = (gd->e_h)(gw,event); |
to determine whether or not an event was handled, so yes your interpretation is right, I think. The gcontainer stuff was always a bit complicated, so not entirely sure how much of it actually ends up being used/important though
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.
agree with dropping the redundant else
though.
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.
Oh, I'm guessing this was my reminder to do something about the F1/Help link, but I didn't decide whether to link to the multidialog help page (potentially useful but counter-intuitive) or allow the caller to supply their own help somehow.
I'll fix it one way or another.
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.
Resolved
|
||
enum multi_done_state { mds_not = 0, mds_ok = 1, mds_cancel = 2, mds_apply = 3 }; | ||
|
||
static int Multi_Button(GGadget *g, GEvent *e) { |
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.
A comment explaining the wiring scheme might be helpful.
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 added some relevant comments
fontforgeexe/multidialog.c
Outdated
return gcd; | ||
} | ||
|
||
static void MultiSyncChoices(MultiDlgSpec *spec) { |
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.
Is this name descriptive? It seems to me that the function just sets defaults.
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'll change it to MultiSetDefaults()
then.
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.
Changed the function name
qstn = (MultiDlgQuestion *) GGadgetGetUserData(g); | ||
free(qstn->str_answer); | ||
qstn->str_answer = GGadgetGetTitle8(g); | ||
if (*qstn->str_answer == '\0') { |
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.
What necessitates this behavior?
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.
Code that deals with GGadgetGetTitle8()
in other places tends to do this. I assume it's because if the content of the field goes from non-empty to empty the GDraw
routines don't null out the string for you. One could always fix the problem there, of course.
fontforgeexe/multidialog.c
Outdated
} | ||
} | ||
|
||
static void MultiCopyStrings(GWindow gw, int strcid, int fbpair) { |
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.
And this function looks like it "syncs".
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.
Also changed this function name
@skef, could you write up the theory of operation for the flow dialog? It would make it a lot easier for me to review that portion of the code. |
I'm not sure what you mean by "theory of operation". At a high level the idea in SAT-analogy syntax is just " GHVBox : GtkGrid :: GFlowBox : GtkFlowBox", although I didn't look at the code of the latter, or really the API, to implement GFlowBox. Beyond that the main point of complication is the integrated "label". If you ignore that label the goal is just to flow the "contents" (GGadgets) like text is flowed, so perhaps left-, right-, or center-aligned or spaced out. And in the opposite direction if there is extra space you might want the content to sit at the top, center, bottom, or be similarly spaced out. The difference is that in the primary direction (horizontal in all existing uses) you're "wrapping" and in the secondary or "opposite" direction you're winding up with however many "lines" result from that wrapping. The code is arranged in terms of "normal" and "oppo" because you might want the primary alignment (the "wrapping") to be vertical or horizontal, but the unused vertical code is currently incomplete. That's basically because I couldn't decide what to do about the label in some of those cases. (It wouldn't be hard to get a label-less vertical flowbox working -- it's already very close.) In horizontal mode there's an optional label that is hard-coded to appear on the left. You can also adjust the label alignment within its "area". Beyond that the main "operational" complication is how it reports its "desired" size. GHVBoxes tend to treat the desired size as the minimum size, so the GFlowBox accordingly tends to report its desired size as the minimum size in its wrapping direction (in this case horizontal) and a single "line" in the opposite direction (because GHVBoxes are generally happy to be larger). The former is typically going to be the width of the largest element. There are some flags in the underscore version of the There's all sorts of things I could go into, like some of the scrollbar calculation weirdness, but that's the basic idea. If I have to write a full detailed "manual" or equivalent code commends to get this integrated I will, but obviously that would take a fair bit of effort and be more documentation than almost any other code in the project. |
@frank-trampe If what you're asking for is more of a quick get-one's-footing primer on how GDraw containers interact with what they contain and what contains them I guess I could write something up. |
Other than the conflict from #4809, I think that this is good to go. |
@jtanx, would you look at the conflict? It seems to me that the right thing is just to remove any case in which text_in_resource is truthy, but I want to be sure I'm not missing anything. |
MultiDlgSpec dlg; | ||
|
||
if ( no_windowing_ui ) { | ||
PyErr_Format(PyExc_EnvironmentError, "No user interface"); |
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.
This leaks the above objects
Rebased to fix that conflict, I still hold my reservations about this, but let's merge. |
Any reason we can't merge this? |
No. I tried to give be498f6 a more helpful message than just a commit list via the squash mechanism:
This is a profoundly useful feature despite reservations and I'm glad @frank-trampe asked the question. |
From the annals of Probably Not Worth It In Retrospect ...
Given the added Techref I think this is pretty self-explanatory. I recommend pulling this over, building the documentation, and pointing your browser to the local files.
I appear to have a bit more window-sizing work to do, as the window described in the techref doesn't quite come up the right size at present, but otherwise things seem to be in good shape. I've tested with both the GDK and X11 back-ends, although more modestly with the latter.
If you don't want to fool around building your own lists/dicts I've made a package with some convenience routines: https://github.com/skef/fontforge_helper . But you can also just test with
an init script something like this:
Mnemonic support is partial. If you add a mnemonic to a checkbox or radio button it works (although there is no visual change, you can press space to select/toggle selection). If you add one to a string or path question the field should get the focus. Mnemonics don't currently work with
GTabSet
s, see #4670.You can also focus a choice-list and use the arrows to change the selection on a non-multiple choice list. There doesn't seem to be a sane way of managing selection by keyboard for a multi-choice-list.
I try to size the initial window reasonably.
The layout choices are mostly the result of trying different options. I wanted to have the dimensions be quite variable so its easier to look at it and the Font or Char View at the same time when desired. Hence
GFlowBox
andGScroll1Box
. The advantage of having the text fields scale to the end of the window is that it's an easy work-around for longer strings -- the user can just tug at the window if they need to.