I wrote this up the other week and never got around to posting it. I've still got the same questions as I haven't had time to go back to UI stuff recently. Any tips, design concepts, etc. appreciated.
After spending time with a group of 4D developers who are sold on no-process-variables on forms, I thought I'd give it a try. I've got a new project I'm starting in V15/V16 with pretty much a clean slate. I like to try going all-in and see what happens. I like the idea in general as it lets you control the interface with what amount to messages. This is a huge win because it's: * Easier to emulate an MVC pattern in 4D. (MVC arose out of the very problems you get form tightly binding code, data and UI.) * Easier to test. * Easier to use with V16's interprocess communications architecture. * It's also a big win if you have problems with process/IP variables getting walked on unexpectedly from different parts of the program. Thinking back, I realize that I don't have this problem in my own code and probably haven't for 20 years. (Careful what you say, I'll get bitten today, you just know it.) But this is a huge deal in a lot of 4D code bases. I've gotten to bury the bodies behind me and move on...not everyone is so lucky. So, yeah, reducing exposed process/IP vars has to go in the "massive win" column. Right, so I'm learning in my usual way...which is to start from scratch, build things one step at a time, dig into the docs when something seems weird, check the list for answers, etc. I'm not one for reading the docs first (who is?) The 4D docs have always been more descriptive than explanatory, if you know what I mean. I'm also not one for immediately diving into an example. I mean, if I don't know how something works, how am I to make sense of a big pile of code? Instead, I like to get into the thick of it, get lost in the weeds...struggle through my mistakes and then dig into some examples. At that point, I can really appreciated advice and good examples. One consequence of this learning style is that you slowly develop a mental model of how things work with inevitably leads you to predict how things will work incorrectly. (This is a good way to find bugs in a beta test, by the way.) As an example, I assumed that these routines would return the same object: OBJECT Get name (Object current) OBJECT Get pointer (Object current) Do they? It depends. I also assumed that you could toss out object scripts completely and trap everything in the form method. Can you? It depends. Right, so now for my observations and questions. As I've already said I tend to misunderstand things as a necessary part of learning how they really work, so please tell me what I've got wrong. As a reminder, my forms have dynamically-bound "form variables" instead of process variables. (For those that don't know, you only get this feature by clearing out the variable name area in the object's property list.) Also, I'm striving to move the code out of scripts entirely. At this point, I'm doing a bunch with listboxes so I've ended up with some listbox-specific behaviors and questions. Oh, in case I come across as being kind of whingy, I'm not. I really like the new features and they're a pleasure to work with. After just a couple of hours I've made heaps of progress...and turned up a bunch of questions. ...I'm on 15.3 OS X 10.10.5, for the record. ------------------------------------------------ OBJECT GET BEST SIZE ------------------------------------------------ OBJECT GET BEST SIZE & listbox columns. WTF? The docs say that you should get a calculation of the ideal width, but I'm getting back what can only be described as weird results. Am I missing a trick, or is this command just wonky? ------------------------------------------------ On Click (etc.) and Form Methods ------------------------------------------------ I ran into this in On Click but it probably applies to most/all/some other events. The form method doesn't seem to get it from some (many?) objects. Click on a listbox and the listbox gets the event and it does *not* bubble up to the form handler meaningfully. Click on a tab control, same thing. Here's what it looks like for a listbox click read from a form method: C_TEXT($current_name) C_POINTER($current_pointer) $current_name:=OBJECT Get name (Object current) // You get "" $current_pointer:=OBJECT Get pointer (Object current) // You get Nil C_TEXT($focus_name) C_POINTER($focus_pointer) $focus_name:=OBJECT Get name (Object with focus) // You get the name of the listbox, no matter where in the listbox you click. $focus_pointer:=OBJECT Get pointer (Object with focus) // You get the variable for the listbox, not a column or header. For a tab control (dynamic variable name populated with strings typed into the form editor), you get nothing relevant. Get name comes up empty while Focus object comes up with something...but not the tab. So, it looks like you need to set scripts on objects to call a project method. In fact, I've already found one case where this must be the case. If you want to do a custom column sort, you'll want to suppress listbox sorting. To do this, you need to return -1 from the On Click Header event handler *in the object itself.* You can push all of the handling code into a project method, but the $0 is defined as property of the listbox, not your method. Like I said, I get into the weeds a bit....In this particular case, I'm not so concerned about the details - you can make it all work okay. What I am wondering about is how people think it makes sense to structure you code for event and form handling. 4D doesn't appear to support having a one-line form method to call a project method...Anyway, for those who have wrestled with this, any thoughts? ------------------------------------------------ OBJECT Get name (Object current) Versus OBJECT Get pointer (Object current) ------------------------------------------------ File under, "well, that's weird." As noted above, I make a lot of guesses that turned out wrong. I assumed that these functions would refer to the same object when called in the same context at the same time. Well, that's not always true. As just pointed out for listboxes: OBJECT Get name (Object current) : Listbox name OBJECT Get pointer (Object current) : Column or header pointer Eh? My generic handler now takes something like this: Test_InputHandler(Form event;OBJECT Get name;Object Get Pointer) Digging in, I discovered that 4D treats the components of a listbox as a kind of nested structure. Call this: ARRAY TEXT($names_at;0) ARRAY POINTER($pointers_at;0) ARRAY LONGINT($pages_al;0) FORM GET OBJECTS($names_at;$pointers_at) The listbox is included in the arrays, but not its columns and headers. For the listbox, you need something like this: If ($error_name="") $form_event:=$1 $object_name:=$2 $passed_pointer:=$3 C_BOOLEAN($clicked_on_a_listbox) C_POINTER($named_pointer) $named_pointer:=OBJECT Get pointer (Object named;$object_name) $clicked_on_a_listbox:=OBJECT Get type(*;$object_name)=Object type listbox If ($clicked_on_a_listbox) ARRAY TEXT($listbox_column_names_at;0) ARRAY TEXT($listbox_header_names_at;0) ARRAY POINTER($listbox_column_pointers_ap;0) ARRAY POINTER($listbox_header_pointers_ap;0) ARRAY BOOLEAN($listbox_column_visible_ab;0) ARRAY POINTER($colmun_styles_ap;0) LISTBOX GET ARRAYS(*;"Test_Findings_listbox";$listbox_column_names_at;$listbox_header_names_at;$listbox_column_pointers_ap;$listbox_header_pointers_ap;$listbox_column_visible_ab;$colmun_styles_ap) .... End if From there, you can use the passed in Object Get pointer value to find what was clicked on inside of the listbox. I guess that there's nothing wrong with this implementation, but it's definitely now what I expected from FORM GET OBJECTS. Perhaps FORM GET OBJECTS only returns objects that are named statically in the Form editor and not bound dynamically at runtime? That would still feel like a bug to me, but at least it would make sense. I should test that out. Does anyone else have any comments on a) other objects that return different vales for get name and get pointer? b) other objects that require secondary inspection? c) ...if it's really about the statically defined and dynamically bound objects? Or is it the difference between variables and dynamically bound runtime variables? [Later] Okay, I tried out a combination of dynamic and statically named items on a form and checked the clicks. It looks like the behavior is that OBJECT Get name and OBJECT Get pointer *only make sense when called from an object script.* This seems to be more-or-less said or implied in the docs. Like I say, I make some wrong turns while figuring things out. I'd guess this is a rabbit hole at least of couple of people have spent serious time in. Any comments or tips? And is there any chance I'm wrong about this? It's rather nice to have a generalized handler that can trap for things rather than having scripts littered around everywhere. Thanks. ********************************************************************** 4D Internet Users Group (4D iNUG) FAQ: http://lists.4d.com/faqnug.html Archive: http://lists.4d.com/archives.html Options: http://lists.4d.com/mailman/options/4d_tech Unsub: mailto:[email protected] **********************************************************************

