David:

Great topic to bring up and good to hear that you're getting back into
using 4D's UI again.

"OBJECT Get name and OBJECT Get pointer *only make sense when called from an
object script.*"
Agreed. Per below, I've run into that behavior and have adjusted my code
design.


"Any comments or tips?"
Some thoughts before I head out into the brutal SoCal weather (what's this
rain stuff - are we in Seattle now?)

I've been dipping my toe in the water on the concept of the "form
controller" which is the moniker that Kirk Brooks came up with. Kirk and I
traded some email on this topic last year and he's done some really nice
things with this technique. Hopefully Kirk will chime in.


In some projects, I've been integrating dynamic variables + form controller
(a project method ending in "_FM") + Objects (OT in my case, so far*).
Initially, I tried catching the events in the _FM but that proved
unworkable. One issue was that the forms had existing code that intercepted
events while other times I wasn't able to trap the event and, finally, as
you have experienced, the tab control *really* threw a wrench in the works.

As much as my preference would have been to be able to use just the _FM, I
ended up removing all doubt by putting a project method in each active
object on the form.


A button, for example, has this code in the OM:

*PROC_FormEvent_Set* (PROC_Handle;*On Clicked*)
*PROC_FocusObjectPtr_Set* (PROC_Handle;*OBJ_GPBN* ("PROP_F029Networkrb"))
*PROPOSALS_FM* (PROC_Handle)


This code is at the top of *PROPOSALS_FM*


C_POINTER($currentObject_P)
$currentObject_P:=*PROC_CurrentObjectPtr_Get* ($h_)

C_TEXT($currentObjectName_T)
$currentObjectName_T:=*PROC_CurrentObjectName_Get* ($h_)

C_POINTER($focusObject_P)
$focusObject_P:=*PROC_FocusObjectPtr_Get* ($h_)

C_LONGINT($formEvent_L)
$formEvent_L:=*PROC_FormEvent_Get* ($h_)

There are other events that I can extract from the Object but, in this
method, all I needed were these four.


Inside the _FM, the code uses a Case statement.

In other situations, instead of calling  *PROC_FormEvent_Set *to capture a
specific event, I've found it easier just to call
*PROC_FormEventValues_Assign* which has this code:


If (*Count parameters*=0)

*     C_LONGINT*($h_)
     $h_:=*PROC_FormEventValues_Assign* (PROC_Handle)

       //How to override
     *PROC_FormEvent_Set* (*PROC_FormEventValues_Assign* (PROC_Handle);*On
Clicked*)


     *TRACE*
End if


*C_LONGINT*($0)


*C_LONGINT*($h_;$1)
$h_:=$1


*PROC_FocusObjectPtr_Set* ($h_;*OBJ_WithFocus* )
*PROC_FocusObjectName_Set* ($h_;*OBJ_FocusName_Return* )

*PROC_CurrentObjectPtr_Set* ($h_;*OBJ_CurrentPtr_Return* )
*PROC_CurrentObjectName_Set* ($h_;*OBJ_CurrentName_Return* )

*PROC_FormEvent_Set* ($h_;*Form event*)

*PROC_gTablePtr_Set* ($h_;*gTablePtr_Get* )

*PROC_CurrentFormPage_Set* ($h_;*FORM Get current page*)


$0:=$h_


*Case of*





*End case*



*PROC_FilterEvent_Set* ($h_;*True*)
This method is used when working with legacy code that has code in object
methods. It is read by code further down the execution cycle, as in:

If (*PROC_FilterEvent_Get* (PROC_Handle))
   *PROC_FilterEvent_Set* (PROC_Handle;*False*)
Else
  *InputMethod* (*gTablePtr_Get* ;*PROC_FormEventValues_Assign* (PROC_Handle
))
End if


And, again, cleaning things up because of other code, I've created
$h_:=*PROC_FormEventValues_Clear* ($h_)



(The If(count parameters)…End if is sample/unit test code that I comment
out once the code has been tested.)

All of those routines are wrappers and $h_ is the handle to an OT object.

I had some concerns about the execution speed, wondering if it was "OK" to
use wrappers that weren't needed but the elapsed time for this method is <
1 millisecond.


To date, this has worked well, even when working in legacy code.


When working from a clean slate, it's worked very nicely.


In the area of dynamic variables, I've gone so far as to use them for the
"standard" form objects but have also used them for OT objects and AreaList
Pro and am pleased with the results.







*Most of my work is in V13 so OT is the weapon of choice. As projects move
out of V13, I will migrate some OT functionality into OB commands.

--
Douglas von Roeder
949-336-2902

On Sat, Jan 7, 2017 at 2:03 PM, David Adams <[email protected]> wrote:

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