Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

The Citizen Toolmaker


Published on

At Camp Smalltalk 2019 in Charlotte, North Carolina saw many great presentations. Cincom's own Carl Gundel from the Cincom Smalltalk engineering team gave an engaging and instructive presentation. Carl explained how to use new features of VisualWorks to create a custom coloring text editor by using the flexible new DocumentEditor and also demonstrated window layout using constraints as an alternative to positioning in the UI Painter. PetitParser was also leveraged in the examples. In addition he also gave a snappy demonstration of the complete Cincom(R) VisualWorks(R) Smalltalk system running on the popular Raspberry Pi computer.

Published in: Software
  • Be the first to comment

  • Be the first to like this

The Citizen Toolmaker

  1. 1. Carl Gundel, Senior Software Engineer The Citizen Toolmaker Camp Smalltalk 2019 - Charlotte, North Carolina
  2. 2. Proprietary & Confidential Pre-canned GUI widgets • All modern development tools come with 'em • Buttons, listboxes, checkboxes, texteditors, etc. • Boring? Mostly, but they're really useful. | @cincomsmalltalk
  3. 3. Proprietary & Confidential Enter: The Citizen Toolmaker Sometimes you need a widget that only you need... So, what to do? Options: • Look at the open source community • Buy a ready made solution • Pay for a custom widget • Develop your own widget | @cincomsmalltalk
  4. 4. Proprietary & Confidential Smalltalk (and you) to the Rescue • “Simple things should be simple. Complex things should be possible.” – Alan Kay • Smalltalk has had source code for widgets from the beginning. • The source code is right there. • Let's do it! | @cincomsmalltalk
  5. 5. Proprietary & Confidential Coloring Text Editor • Let's create a text editor that will colorize text in more than one format. • We will leverage the new DocumentEditor and constraints layout features of Cincom® VisualWorks® 8.3. • Let's also use the popular PetitParser to generate tokens for coloring the text. | @cincomsmalltalk
  6. 6. Proprietary & Confidential The Art of War • In the future, all wars will be conducted using robots. Training for this future will involve advanced robot war simulation. • The defense department has specified that our training simulation must use... | @cincomsmalltalk
  7. 7. Proprietary & Confidential Why RobotWar? | @cincomsmalltalk • I mean... heck, it's a game for programmers! The stuff of the cyber- future, from 1981. • We could do Crobots ( It's fun, but that's just C. :-P • It's a great launching point for implementing a version of the game in Smalltalk later if we want! An exercise left to the reader. • We can easily add support for coloring other formats easily enough, so we will for good measure.
  8. 8. Proprietary & Confidential A Survey of Battle Language | @cincomsmalltalk • Battle Language (yep, that's what it's called) is similar to BASIC. • 34 canned variable names/registers, some with special purposes • Registers A..Z (X are Y are reserved), and also AIM, RADAR, SHOT, DAMAGE, SPEEDX, SPEEDY, RANDOM. INDEX acts as a pointer into A..Z • Reserved words: TO, IF, GOTO, GOSUB, ENDSUB • Special characters: ] = # < > + - * / ; • And integers - Battle Language has no strings. • It's a very small language so it is an ideal example for this presentation.
  9. 9. Proprietary & Confidential Example Battle Language Coloring | @cincomsmalltalk SCAN AIM + 5 TO AIM ; MOVE GUN AIM TO RADAR ; SEND RADAR PULSE LOOP IF RADAR < 0 GOSUB FIRE ; TEST RADAR GOTO SCAN FIRE 0 - RADAR TO SHOT ; FIRE THE GUN ENDSUB
  10. 10. Proprietary & Confidential Parsing the Code with PetitParser | @cincomsmalltalk • Note: This is not a PetitParser tutorial, but it should suffice as a pedagogical example. :) • We will not write a compiler here. We are only interested in tokens. • Load the PetitParser parcel from the VisualWorks Transcript System menu.
  11. 11. Proprietary & Confidential Laying Out the Window | @cincomsmalltalk First we layout the window for our editor with: • A toolbar (file new, open, save, a clipboard) • A dropdown for choosing format • A DocumentEditor • A status line at the bottom
  12. 12. Proprietary & Confidential Layout Using Constraints | @cincomsmalltalk • Normally in VisualWorks you would lay out your widgets using the position tab. • This is not necessary if using constraints, so I only just dragged and positioned. • The constraints will allow us to position widgets relative to each other and to any arbitrary value as desired, not just to the edges of the window or subcanvas.
  13. 13. Proprietary & Confidential Some Helper Code for Using Constraints | @cincomsmalltalk We need to use a ConstraintWrapper for each widget, so we add this helper method to our application model. constraintsFor: aComponent left: leftValue right: rightValue top: topValue bottom: bottomValue | wrapper newWrapper container component | wrapper := aComponent component. container := wrapper container. component := wrapper component. newWrapper := ConstraintWrapper new. newWrapper constraints left: leftValue right: rightValue top: topValue bottom: bottomValue. wrapper become: newWrapper. (aComponent component) component: component; container: container
  14. 14. Proprietary & Confidential An Example Constraint | @cincomsmalltalk self constraintsFor: self editorComponent left: [0] right: [self clientWidth ] top: [self languagesComponent boundingFrame top + self languagesComponent widget artist height + 8] bottom: [self statusComponent component boundingFrame top] - left: is the left edge of the window - right: is the right edge of the window - top: is the bound to the position of the dropdown list above the editor - bottom: is bound to the top edge of the status line All the widgets a laid out this way. This is very flexible. For example clientWidth can be a dynamic value that allows us to hide/show a side bar with more widgets.
  15. 15. Proprietary & | @cincomsmalltalk Here is the complete layout method called from postOpenWith: setupConstraints self constraintsFor: self editorComponent left: [0] right: [self clientWidth ] top: [self languagesComponent boundingFrame top + self languagesComponent widget artist height + 8] bottom: [self statusComponent component boundingFrame top]. self constraintsFor: self statusComponent left: [8] right: [self clientWidth] top: [self builder composite boundingFrame bottom - 16] bottom: [self builder composite boundingFrame bottom - 4]. self constraintsFor: self languagesComponent left: [self clientWidth - 200] right: [self clientWidth - 4] top: [self toolbarComponent boundingFrame bottom] bottom: [self toolbarComponent boundingFrame bottom + 24]. self constraintsFor: self languagesLabel left: [self languagesComponent component boundingFrame left - 60] right: [self languagesComponent component boundingFrame left - 4] top: [self toolbarComponent boundingFrame bottom + 6] bottom: [self toolbarComponent boundingFrame bottom + 24]. self builder window component invalidateLayout
  16. 16. Proprietary & Confidential DocumentEditor - What It Does For Us | @cincomsmalltalk Rich functionality: • Arbitrary and multiple fonts, sizes, bold, italics, etc. per document • Coloring of text and background • Tooltips in the text • Annotations - inserting information cohabitation in the editor without being part of the document • Live text makes the document interactively clickable • Bulleted lists • Embedded images • Paragraphs • Bi-directionally of text, support for multiple languages
  17. 17. Proprietary & Confidential Next Steps | @cincomsmalltalk • Make some simple tokenizing parsers using PetitParser. • Create a subclass of DocumentEditor called ColoringDocumentEditor and support classes called ColoringToken and BattleLanguageParser. • Add a few methods to apply those tokenizing parser.
  18. 18. Proprietary & Confidential Make Some Token Parsers Using PetitParser | @cincomsmalltalk Disclaimer - This is not a PetitParser tutorial. There are tutorials on the web and on YouTube. We need to be able to grab each kind of item from text, so we use PetitParser for this. Here is a parser for grabbing Battle Language keywords out of text for example: 'TO' asParser / 'IF' asParser / 'GOTO' asParser / 'GOSUB' asParser / 'ENDSUB' asParser Here is another one for grabbing arbitrary identifiers: #letter asParser , #letter asParser asParser star
  19. 19. Proprietary & Confidential All the Battle Language Parsers | @cincomsmalltalk Each of these parsers is constructed very simply. createTokenParsers "Create all the token parsers we need for Battle Language" reserved := 'TO' asParser / 'IF' asParser / 'GOTO' asParser / 'GOSUB' asParser / 'ENDSUB' asParser. identifier := #letter asParser , #letter asParser asParser star. integer := #digit asParser plus. specialCharacters := $] asParser / $= asParser / $# asParser / $< asParser / $> asParser / $+ asParser / $- asParser / $* asParser / $/ asParser
  20. 20. Proprietary & Confidential Create a Subclass of DocumentEditor Called ColoringDocumentEditor | @cincomsmalltalk Smalltalk.UI defineClass: #ColoringDocumentEditor superclass: #{UI.DocumentEditor} indexedType: #none private: false instanceVariableNames: '' classInstanceVariableNames: '' imports: '' category: '' Also we need to add a ColoringDocumentEditorSpec class to make UILookPolicy aware of our new widget. It needs only this method: dispatchTo: policy with: builder ^policy coloringDocumentEditor: self into: builder And we need these new methods for UILookPolicy: coloringDocumentEditor: spec into: builder | model component | model := spec modelInBuilder: builder. component := self coloringDocumentEditorClass model: model. ^self documentView: spec component: component into: builder coloringDocumentEditorClass ^ColoringDocumentEditor
  21. 21. Proprietary & Confidential Create the ColoringToken Class | @cincomsmalltalk We need an object for each word or symbol that we are coloring: Smalltalk.UI defineClass: #ColoringToken superclass: #{Core.Object} indexedType: #none private: false instanceVariableNames: 'string selector start stop modifier ' classInstanceVariableNames: '' imports: '' category: '‘ • string is the word, symbol or token to colorize. • selector represents the kind of style to apply to the string. • start and stop are the position of the string in the document. • modifier is a block that can be used to add additional transformations.
  22. 22. Proprietary & Confidential Interesting ColoringToken Methods | @cincomsmalltalk This method applies the token's color (style actually) to the document: highlightDocument: aDocument offset: offset | aDocumentStyle color emphasis | aDocumentStyle := (aDocument nodeAtPositionAfter: 0 ifAbsent: [ self halt: 'Bad document format' ]) value style copy. emphasis := self baseEmphasis, (self perform: self selector). color := (emphasis detect: [ :each | each key = #color ] ifNone: [ #color -> ColorValue black]) value. aDocumentStyle color: color. (emphasis includes: #underline) ifTrue: [ aDocumentStyle adornment: #underline ]. aDocument replaceBetween: start + offset - 2 and: stop + offset - 1 with: (modifier value: self string) style: aDocumentStyle Several xxxxxxStyle methods supply the color and potentially other enhancements for example: integerStyle ^Array with: #color -> ColorValue red
  23. 23. Proprietary & Confidential Creating the BattleLanguageParser Class | @cincomsmalltalk Smalltalk.UI defineClass: #BattleLanguageParser superclass: #{UI.NullColoringParser} indexedType: #none private: false instanceVariableNames: 'reserved identifier integer specialCharacters ' classInstanceVariableNames: '' imports: '' category: '' The reserved, identifier integer and specialCharacter ivars are just there to cache the PetitParser token parsers for performance reasons. There is no other state.
  24. 24. Proprietary & Confidential Interesting BattleLanguageParser Methods | @cincomsmalltalk match: aString do: aBlock ^self parser matchingSkipRangesIn: aString do: aBlock tokensFrom: aString | tokens | tokens := OrderedCollection new. self match: aString do: [:each | | string token | string := (aString copyFrom: each first to: each last) asString. token := ColoringToken on: string start: each first stop: each last. (self reserved parse: string) isPetitFailure not ifTrue: [token uppercase selector: #reservedStyle] ifFalse: [ (self identifier parse: string) isPetitFailure not ifTrue: [token uppercase selector: #identifierStyle] ifFalse: [ (self integer parse: string) isPetitFailure not ifTrue: [token selector: #integerStyle] ifFalse: [ (self specialCharacters parse: string) isPetitFailure not ifTrue: [token selector: #specialCharactersStyle]]]]. tokens add: token]. ^tokens
  25. 25. Proprietary & Confidential Making use of the ColoringToken and BattleLanguageParser classes | @cincomsmalltalk We add this method to invoke coloring: colorize self invalidateSelectionWhile: [self colorizeVisibleTextOn: self graphicsContext]. flow cacheReset. self invalidateNow In our ColoringDocumentEditor class we override the following methods and add self colorize to them: clipboardPaste super clipboardPaste. self colorize. self invalidateNow insert: aString super insert: aString. self colorize scrollBy: aPoint super scrollBy: aPoint. self colorize continued next slide...
  26. 26. Proprietary & Confidential More Making use of the ColoringToken and BattleLanguageParser classes | @cincomsmalltalk This method figures out the part of the document that is currently visible and calls highlightDocument:from:to: on it. colorizeVisibleTextOn: aGraphicsContext | range | range := self rangeOfTextIntersecting: self bounds. range = Point zero ifTrue: [ ^self ]. self highlightDocument: self document from: (range x + 1 max: 1) to: (range y + 1 min: self document size) This method calls highlightedTokensDocument:from:to: to apply the parsers and make ColoringTokens. Then it tells those tokens to colorize. highlightDocument: aDocument from: start to: stop (self highlightedTokensDocument: aDocument from: start to: stop) do: [ :each | each highlightDocument: aDocument offset: start ] highlightedTokensDocument: aDocument from: start to: stop ^self colorizingParser tokensFrom: (aDocument copyFrom: start to: stop - 1) That's it in a nutshell!
  28. 28. Thank You! Any questions?
  29. 29. Cincom, the Quadrant Logo, Cincom Smalltalk, Cincom OjbectStudio and Cincom VisualWorks are trademarks or registered trademarks of Cincom Systems, Inc. Microsoft is a registered trademark of Microsoft Corporation. All other trademarks belong to their respective companies. ©2019 Cincom Systems, Inc. All Rights Reserved