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]
**********************************************************************

Reply via email to