LiveCode for FM ManualCustom ComponentsHow do I add features to the Annotate Image Dialog

How do I add features to the Annotate Image Dialog

LiveCode for FM comes with some build in custom components, but what if these provide almost what you want but not quite?

You can add features to the built in custom components, or create your own custom components from scratch using the LiveCode IDE.

In this lesson we will extend the Annotate Image Dialog with the ability to add text labels to the image.

Make a copy of the Annotate Image Dialog

Each custom component is a LiveCode stack, that implements certain required features in order to run within FileMaker.

At the moment you can't clone built in custom components within the Workspace so start by downloading the attached stack.

Open the stack

Unzip the file and open 'stack.livecode' in the LiveCode IDE.

Rename the stack

LiveCode requires stacks that are open at the same time to have unique names so the first step is to rename the stack.

  • Open the Stack Inspector from the Object Menu
  • Set the Name to "annotateImageText"

Update the UI

The UI for the original Annotate Image Dialog has a button that allows the user to add a maker. Add a second button that will allow the user to add text.

  1. Ensure you are in Edit mode
  2. Add a button
  3. Open the Object Inspector
  4. Set the Name to "Add Text"

The button will call the AddText command, which will be implemented on the Card Script.

  1. Open the Object Script
  2. Set the button script to
on mouseUp
   AddText
end mouseUp

The AddText command

The AddText command will create a field that the user can add text to.

  1. Open the Card Script
  2. Add the AddText command
command AddText
   local tLabel
   
   lock screen

   create field ("text" && (the number of fields of group "editor") + 1) in group "editor"
   set the width of it to 150
   set the showBorder of it to false
   set the backgroundColor of it to 255,255,255
   set the location of it to the location of image "image"
   set the behavior of it to the long id of button "Text Behavior" of me

   unlock screen
   
end AddText
  • Lock the screen to temporarily prevent screen updates. This allow you to make multiple changes without the user seeing each transition, all the changes are shown at once when the screen is unlocked.
  • Create a new field. Each field is named 'text x'. To ensure the fields have unique names calculate the name of the new field using the current number of fields + 1.
  • Set the width, border and background color off the field.
  • Set the location of the field to the location of the image to center it.
  • Set the behavior of the field. We will implement the behavior next.

Create the behavior for text fields

  • Add a button to hold the behavior script
  • Set the Name to "Text Behavior"

What is a behavior?

Behaviors are a method to create common functionality between objects without duplicating the scripts.

An object with a behavior set will act as though its script was set to the script of the behavior button or stack. If multiple objects share the same behavior, each will have its own set of script local variables. Any references to me, the owner of me, and so on, will resolve to the child object currently executing.

The behavior script

The fields/text areas have to behave differently in different states.

Editable

  • Opaque
  • Border
  • Can be edited

Display

  • Transparent
  • No border
  • Can't be edited
  • Can be moved around

The mouseDown handler

To allow fields to be moved around we will use the grab command. Locked (lockText property set to true) receive the mouseDown message so we can grab the field on mouseDown, the field will remain grabbed until the user releases the mouse button, at which point the control will receive a mouseUp message.

Open the script for the "Text Behavior" button and add the mouseDown handler.

on mouseDown
   grab me
end mouseDown

The mouseUp handler

When the field receives the mouseUp message we want to make it editable. Add the mouseUp handler.

on mouseUp
   setEditable true
end mouseUp

The closeField and exitField handlers

When you are stop editing the field, by focusing away from the field, we want to make the field non-editable.

When a field loses focus is receives one of two messages

  • closeField - Sent to a field when the focus is being removed from that field and the field's content has changed.
  • exitField - Sent to the field with the selection when the selection is being removed from the field, and its contents have not changed.

Add handler for these two messages.

on closeField
   setEditable false
end closeField

on exitField
   setEditable false
end exitField

The setEditable command

The setEditable command changes the state of the field by setting properties. It takes one parameter, pEditable, which is either true or false.

In addition when a field is set to non-editable we make it tall enough to show all the text.

Add the setEditable command.

command setEditable pEditable
   if pEditable then
      // Set the properties of the field to make it editable
      set the showFocusBorder of me to true
      set the opaque of me to true
      set the lockText of me to false
   else
      // Set the properties of the field to make it non-editable
      set the showFocusBorder of me to false
      set the height of me to the formattedHeight of me
      set the opaque of me to false
      set the lockText of me to true
   end if
end setEditable

Hide the Text Behavior button

The "Text Behavior" button should not be visible to the user so set its Visible property to false.

Allow the user to select away from a text field

Next we need to ensure users can click away from a text field.

A card can receive mouseUp messages, just like the markers and text fields. If a user clicks somewhere that is not a marker or text field we want to unfocus all the objects.

Add a mouseUp handler to the Card Script, this will be triggered if the user clicks on the card itself and will unfocus all the controls.

on mouseUp
   focus on nothing
end mouseUp
Click to copy

Update the AddMarker command

Behaviors are referred to by long id, so if you change the name of a stack you need to ensure that any existing behaviors can still be resolved. Currently the behavior for marker is set on the template control, since we have changed the name of the stack this will need updated.

We will update the AddMarker command to set the behavior when we create new markers.

Open the Card Script from the Object menu and add a line of code that set the behavior of the new marker.

command AddMarker
   local tMarker
   repeat with tMarker = 1 to the number of groups of group "editor"
      if the hidden of group tMarker of group "editor" then
         show group tMarker of group "editor" at the location of image "image"
         exit AddMarker
      end if
   end repeat
   
   lock screen
   clone group "Marker 1" of group "editor"
   set the location of it to the location of image "image"
   set the label of button "marker number" of it to the number of groups of group "editor"
   set the name of it to ("Marker" && the number of groups of group "editor")
   set the behavior of it to the long id of button "Marker Behavior" of me
end AddMarker

Update the stack script

We also need to update the stack script to load this plugin and to store and load any text along with the image.

Open the Stack Script, we will be updating the FileMakerAction handler.

Note: The FileMakerAction handler is called whenever the component is called from FileMaker via LC.

Load the plugin

Firstly we will update the code that makes the plugin visible. This is done using the modal command. We need to update that line to launch the "annotateImageText" stack.

Update the code to

modal stack "annotateimagetext"

Storing text field details

Next we will store any text fields. Find the section of the stack script that stores the maker information, it should be lines 66 to 72

repeat with tMarker = 1 to the number of groups of group "editor"
   ...

end repeat

We need to do the same for text fields, add this code after the marker saving code.

For makers we only store the location, for text fields we also need to store the text.

 repeat with tText = 1 to the number of fields of group "editor"
      if the visible of field tText of group "editor" then
         put the location of field tText of group "editor" into tNewFileDataA["textfields"][tText]["location"]
         put the text of field tText of group "editor" into tNewFileDataA["textfields"][tText]["text"]
         add the hScroll of group "scroller" to item 1 of tNewFileDataA["textfields"][tText]["location"]
         add the vScroll of group "scroller" to item 2 of tNewFileDataA["textfields"][tText]["location"]
      end if
   end repeat

Loading text field details

Next we need to load the text field information that is stored along with the image.

Go to the section of the stack script that loads the marker information. It should end on line 55.

We need to do the same for text fields

  • If there is not a field create it
  • Set the location of the field

Firstly we find out how many text fields there are. Add the variable declaration and code to the section that gets the number of markers.

local tTotalMarkers, tTotalTextFields
put max(the number of elements of tFileDataA["markers"], the number of groups of group "editor") into tTotalMarkers
put max(the number of elements of tFileDataA["textfields"], the number of groups of group "editor") into tTotalTextFields

Then add this code after the marker loading code.

local tTextField
repeat with tTextField = 1 to tTotalTextFields
	if there is not a field tText of group "editor" then
		create field tText in group "editor"
		set the width of it to 150
		set the showBorder of it to false
		set the opaque of it to false
		set the lockText of it to true
		set the text of it to tFileDataA["textfields"][tText]["text"]
	end if
         
	if tFileDataA["textfields"][tText]["location"] is not empty then
		show field tText of group "editor" at tFileDataA["textfields"][tText]["location"]
	else
		hide field tText of group "editor"
	end if
end repeat

Adding the stack to FileMaker as a Custom Component

Now we are finished updating the stack in LiveCode we want to add it to FileMaker as a custom component.

 

  • Start up FileMaker
  • Open the LiveCode for FM solution
  • Click the "Custom Components" button to open the Workspace
  • Click the "+" button
  • Navigate to your stack and select it
  • Set the name of the custom component to "annotateImageText"

The "annotateImageText" component will appear in the list and will be automatically selected. When a new custom component is added a default guide is created for it. You can update the guide, which is written in Markdown, by clicking the edit button which opens the folder containing the custom component and editing the 'guide.md' file in the folder.

When you are done click the "Close" button to return to the solution.

Testing the Dialog

To test the dialog create a new solution that contains a container and a button that executes

LC("annotateimagetext";<container field>)
  • Drag an image into the container
  • Click the button to show the dialog
  • Click the "Add Text" button
  • An editbable field is created
  • Add some text to the field

Click away from the field to stop editing

  • Click and hold on the field to drag it to the location you want
  • Release the mouse button to place the field
  • Click away from the field

Click the "Done" button to return to the solution. The container will be updated with the annotated image.

Further ideas

There are lots of ways you could take this custom component further

  • Allow the user to add graphics
    • circle to highlight certain areas
    • arrows
  • Provide formatting options
    • text color
    • font
    • marker border color
    • marker background color

Resources

How do I create a custom component?

How to I use the PDF Selection Dialog?

Have a look at the 'Remote Time' dialog interactive tutorial. You can find this in your LiveCode account.

0 Comments

Add your comment

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.