FAQ


Table of Contents:

Events

Processes & Multi-Threading

Widget behavior

Team development

Garbage collection


Events

Q: Why are my event handlers not triggered by the initialize method?

A: This no longer happens. The #initialize method now behaves just like any other method.

Q: Why is my event handler called twice, when it overrides an event from my super class?

A: This no longer happens. When a subclass overrides a trigger, only the subclass trigger will be used.

Q: How do I get my event handler called each and every time it is triggered, even if they are triggered in one frame?

A: You can access the suppressed events in your handler using #withDroppedEventsDo:, for example:

onEvent: myEvent
  "Handle myEvent with all events that have been received, but not handled so far."
  myEvent withDroppedEventsDo:[:evt|
    "... whatever you'd like to do here..."
  ].

Q: What exactly is the "previous" value when using a change event?

A: When using a change event where the previous value(s) are relevant you need to set up an event method such as this:

onFieldChanged: newValue from: oldValue event: event
   "A value has changed. Update appropriately."
   <on:fieldChanged>

The newValue will be the current value of the field; the oldValue will be the value that the field had when the method was last invoked. In other words, it was the last invokation's newValue. The reason for this behavior is obvious: It allows you treat many changes that have happened as a single change from the old to the new value.

If you need to distinguish which intermediate values had been assigned to this field you can do this by asking the event for its previous' (called "dropped") events, e.g.,

  event withDroppedEventsDo:[:evt|
    Transcript cr; show: 'Changed from: ', 
        evt arguments second "the previous value",
        'to', evt arguments first "the next value"
  ].

With the above pattern you can enumerate all of the intermediate changes to the field.

Q:Is there a way of handling errors like events?

A: Yes. Players can handle errors in a way that allows other clients to handle these errors as events. In order to do this, a player must handle errors like here:

 ["do something"] on: Error do:[:ex|
   self handleError: ex ifNone:["default response"].
 ].

A client (costume or other interested party) can now respond to an #error event such as here:

 onPlayerError: anError
   <on: error in: player>
   Transcript cr; show: anError.
   anError return. "from the protected block doing nothing"

There are some important implications for handling errors in the above way, including:

Be aware that this facility is currently experimental.


Processes & Multi-Threading

Q: Tweak uses processes for its own purposes. How do I make it interact with multi-threaded applications such as web-clients?

A: The best way to have Tweak interact with other processes is to use events. In other words, for a process to communicate with some Tweak object it should signal an event and the Tweak object should react appropriately. For example:

Server>>runServerLoop
  "run a server loop in a separate process"
  process := [
     [true] whileTrue:[
       msg := self readMessage.
       handler signal: #messageReceived with: msg.
     ].
  ] forkAt: Processor lowIOPriority.

CMessageHandler>>onMessageReceived: aMessage
  "We have received a new message, process it."
  <on: messageReceived>
  Transcript cr; show: 'The new message is: ', aMessage.

Note that you should use the withDroppedEventsDo: method described above to not miss any message.


Widget behavior

Q: When accepting a new entry in an editable drop down list, the new entry does not get automatically added to the list. Why?

A: There are many uses for editable drop down lists besides simply adding a new entry. For example, one could use the new entry to match it against the closest entry in the list. But even if one would add a new entry various open questions remain such as: Are duplicates allowed? Should comparison be done based on identity? Equality? Etc.

Because of this, clients need to add the new item to the list by themselves, for example:

onDropDownListAccept
  "The user accepted input the drop down list. Add the new item to list."
  <on: accept in: dropDownList>
  | newValue |
  newValue := dropDownList value.
   "Avoid duplicate entries"
  (dropDownList includes: newValue) 
    ifFalse:[dropDownList add: newValue].

Q: What are the tweak events generated by a mouse scroll wheel?

A: Scroll wheel events are currently somewhat buggy since the VMs generate strange events for them. You need to do something like here:

setupCostume
  "Set up my costume"
  super setupCostume.
  "Signal scrollWheel events for ctrl-down/up"
  self onKeyStroke: #(ctrl 30) signal: #scrollWheel.
  self onKeyStroke: #(ctrl 31) signal: #scrollWheel.

The events can then be handled like this:

onScrollWheel: event
  <on: scrollWheel>
  event keyValue = 30 ifTrue:["scroll up"].
  event keyValue = 31 ifTrue:["scroll down"].

Q: How do I scroll a text editor or a list programmatically?

A: The following properties control scroll behavior:

In addition to the above properties, the utility method scrollToShow: can be used to make either a player or a rectangle visible.

Q: How do I avoid that a text editor scrolls to 0,0 when I change its contents?

A: Like here:

  "Change the text in textEditor without changing scroll"
  oldScroll := textEditor scrollOffset.
  textEditor contents: aText.
  textEditor scrollOffset: oldScroll.

Q: My label's text is cut off!

A: Enable shrink wrapping to automatically resize the labelL

 myLabel hResizing: #shrinkWrap

Team development

Q: I have heard Tweak uses Monticello for development. Why does it use an update stream?

A: Update streams and Monticello packages serve different purposes. Monticello packages are a wonderful way of managing source code in a distributed team but it is a poor way to deal with incremental changes, dynamic reformulations of an existing image etc. This is what we use the update stream for.

Q: How do updates and packages relate, e.g., what happens when I hit the "update" button?

A: The update stream is always considered first. The update stream contains two types of updates: configuration maps and other updates. Configuration maps are simply sets of specific package versions that need to be loaded in a particular order to function properly. By "other" updates we mean those that might be needed to fix things in a running system (class reshapes, doIts etc).

Once the update stream has been processed, the system looks for the latest versions of any packages that it requires. Those will be the packages that have changed since the latest configuration loaded via the update stream.

This scheme has the advantage that we only need to post updates (configuration maps or other) when there is a need to - by default updating will just get you the latest packages and only if there is specific need, we will issue an update.

Q: How do I post a Monticello configuration map in an update stream?

A: This is a newly added feature, please have a look at the full explanation.


Garbage collecting

Q: How do I remove instances of tweak objects after tweak is closed, which survive a garbage collect? / There are instances of CWorldPlayer or other Tweak Objects, but there is no tweak window open.

A:

Warning! This snippet will cleanup some known places where CPLayer references might be hiding. Do this only in a Morphic workspace after closing Tweak! Every running Tweak instance will become unusable:

World clearCommandHistory.
ScriptScheduler allSubInstancesDo: [:ea | ea shutDown].
Island allSubInstancesDo: [:ea | ea flushGlobals].
3 timesRepeat: [
	Smalltalk garbageCollect.
	Object flushDependents].
Undeclared removeUnreferencedKeys.
StandardScriptingSystem initialize.
(Object classPool at: #DependentsFields) size > 1 ifTrue: [
	(Object classPool at: #DependentsFields) inspect.
	self error:'Still have dependents'].
Undeclared isEmpty ifFalse: [Undeclared inspect.
	self error:'Please clean out Undeclared'].

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(space above intentionally left blank)