From 30684aef12ad34873e9332757e78ffb6e49752b6 Mon Sep 17 00:00:00 2001 From: Tom Beckmann Date: Mon, 3 Jan 2022 22:03:03 +0100 Subject: [PATCH] tutorial: fix descriptions, port to new insert cursor, provide more compelling example at the end --- .../SBMorphExampleCase.class.st | 6 ++ packages/Sandblocks-Core/SBEditor.class.st | 10 --- .../SBForceMoveDecorator.class.st | 2 +- .../Sandblocks-Core/SBInputMapping.class.st | 12 +++- packages/Sandblocks-Core/SBWelcome.class.st | 7 +- .../SBExampleMorph.class.st | 41 ++++++++++++ .../SBTutorialStep.class.st | 66 +++++++++++++++---- 7 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 packages/Sandblocks-Tutorial/SBExampleMorph.class.st diff --git a/packages/Sandblocks-Babylonian/SBMorphExampleCase.class.st b/packages/Sandblocks-Babylonian/SBMorphExampleCase.class.st index 14206dee..6f4154cc 100644 --- a/packages/Sandblocks-Babylonian/SBMorphExampleCase.class.st +++ b/packages/Sandblocks-Babylonian/SBMorphExampleCase.class.st @@ -54,6 +54,12 @@ SBMorphExampleCase >> initialize [ cellGap: 4 ] +{ #category : #'as yet unclassified' } +SBMorphExampleCase >> intoWorld: aWorld [ + + self status = #restartOnSave ifTrue: [self run] +] + { #category : #'as yet unclassified' } SBMorphExampleCase >> manualRestart [ diff --git a/packages/Sandblocks-Core/SBEditor.class.st b/packages/Sandblocks-Core/SBEditor.class.st index faba0d46..c135b223 100644 --- a/packages/Sandblocks-Core/SBEditor.class.st +++ b/packages/Sandblocks-Core/SBEditor.class.st @@ -20,7 +20,6 @@ Class { 'history', 'dragHelper', 'cursor', - 'activateForcesButton', 'hoverHighlight', 'suggestAlways', 'activePlugins' @@ -284,9 +283,6 @@ SBEditor >> buildCommandBar [ shortcut: (self class anyShortcutForAction: #promptAddArtifact) do: [self promptAddArtifact]; balloonText: 'Add artifact'); - addMorphBack: (activateForcesButton := SBButton new - icon: SBIcon iconMagnet shortcut: (self class anyShortcutForAction: #activateForces) do: []; - balloonText: 'Activate Layouting'); addMorphBack: (Morph new height: 0; color: Color transparent; @@ -911,12 +907,6 @@ SBEditor >> isSingleArtefactView [ ^ false ] -{ #category : #accessing } -SBEditor >> keepForcesActivated [ - - ^ activateForcesButton ifNotNil: #pressed ifNil: [false] -] - { #category : #events } SBEditor >> keyboardFocusChange: aBoolean [ diff --git a/packages/Sandblocks-Core/SBForceMoveDecorator.class.st b/packages/Sandblocks-Core/SBForceMoveDecorator.class.st index 5aa48cb2..4c63d54e 100644 --- a/packages/Sandblocks-Core/SBForceMoveDecorator.class.st +++ b/packages/Sandblocks-Core/SBForceMoveDecorator.class.st @@ -445,7 +445,7 @@ SBForceMoveDecorator >> step [ self isDragging ifTrue: [self panWhenNearEdge]. - (forceSteps > 0 or: self morph sandblockEditor keepForcesActivated) + forceSteps > 0 ifTrue: [self isForceCoordinator ifTrue: [self coordinateForces]] ifFalse: [ super step. diff --git a/packages/Sandblocks-Core/SBInputMapping.class.st b/packages/Sandblocks-Core/SBInputMapping.class.st index 37ed9b35..12bb4de3 100644 --- a/packages/Sandblocks-Core/SBInputMapping.class.st +++ b/packages/Sandblocks-Core/SBInputMapping.class.st @@ -54,8 +54,7 @@ SBInputMapping >> actionFor: anEvent mode: aMode do: aBlock ifNone: anotherBlock registerBlock := [:shortcut :action | | ret |(shortcut asSandblockShortcut matchesEvent: anEvent mode: aMode) ifTrue: [(ret := aBlock value: action) ifTrue: [^ ret]]]. - self registerDefaultShortcuts. - self class shortcutProviders do: [:provider | provider registerShortcuts: self]. + self registerAllShortcuts. ^ anotherBlock value ] @@ -101,6 +100,13 @@ SBInputMapping >> inputShortcut: aShortcut do: aSymbol [ self shortcut: aShortcut modes: #(input) do: aSymbol ] +{ #category : #'shortcut execute' } +SBInputMapping >> registerAllShortcuts [ + + self registerDefaultShortcuts. + self class shortcutProviders do: [:provider | provider registerShortcuts: self] +] + { #category : #'shortcut execute' } SBInputMapping >> registerDefaultShortcuts [ @@ -135,7 +141,7 @@ SBInputMapping >> shortcuts [ ^ OrderedCollection streamContents: [:stream | registerBlock := [:shortcut :action | stream nextPut: shortcut asSandblockShortcut -> action]. - self registerDefaultShortcuts] + self registerAllShortcuts] ] { #category : #'event handling' } diff --git a/packages/Sandblocks-Core/SBWelcome.class.st b/packages/Sandblocks-Core/SBWelcome.class.st index 734ab5b9..0eaeca4b 100644 --- a/packages/Sandblocks-Core/SBWelcome.class.st +++ b/packages/Sandblocks-Core/SBWelcome.class.st @@ -32,9 +32,10 @@ SBWelcome >> initialize [ openMorphInView: SBScmModule example; openMorphInView: SBJsModule example; openMorphInView: (SBStPlayground example name: 'Smalltalk Workspace')]); - addMorphBack: (SBButton new icon: SBIcon iconVine label: 'Use Vim-like Shortcuts' do: [ - self sandblockEditor useVimInput. - SBEditor useInputMapping: SBVimInputMapping]); + addMorphBack: (SBButton new + icon: SBIcon iconCog + label: 'Open Preferences' + do: [self sandblockEditor openMorphInView: SBPreferencesEditor new]); addMorphBack: (SBButton new icon: SBIcon iconCloudDownload label: 'Update to bleeding-edge' diff --git a/packages/Sandblocks-Tutorial/SBExampleMorph.class.st b/packages/Sandblocks-Tutorial/SBExampleMorph.class.st new file mode 100644 index 00000000..9f2fb8b7 --- /dev/null +++ b/packages/Sandblocks-Tutorial/SBExampleMorph.class.st @@ -0,0 +1,41 @@ +Class { + #name : #SBExampleMorph, + #superclass : #Morph, + #category : #'Sandblocks-Tutorial' +} + +{ #category : #'as yet unclassified' } +SBExampleMorph >> drawOn: aCanvas [ + + aCanvas fillRectangle: self bounds fillStyle: (self color alpha: 0.4) +] + +{ #category : #'as yet unclassified' } +SBExampleMorph >> example [ + + SBMorphExample + setUp: [self class new] + cases: {SBMorphExampleCase name: 'example 1' caseBlock: [:mt | mt]} + extent: 300 @ 300 +] + +{ #category : #'as yet unclassified' } +SBExampleMorph >> initialize [ + + super initialize. + + self extent: 50 asPoint. + self color: Color red +] + +{ #category : #'as yet unclassified' } +SBExampleMorph >> step [ + + self position: 60 @ ((Time millisecondClock / 100) sin * 30 + 80) +] + +{ #category : #'as yet unclassified' } +SBExampleMorph >> stepTime [ + + ^ 0 +] diff --git a/packages/Sandblocks-Tutorial/SBTutorialStep.class.st b/packages/Sandblocks-Tutorial/SBTutorialStep.class.st index 302fac9d..027bdaa4 100644 --- a/packages/Sandblocks-Tutorial/SBTutorialStep.class.st +++ b/packages/Sandblocks-Tutorial/SBTutorialStep.class.st @@ -31,9 +31,12 @@ SBTutorialStep class >> copyClass: aClass in: anEditor [ copyReplaceAll: aClass category with: 'UserObjects'). newClass copyAllCategoriesFrom: aClass. - anEditor open: (SBStClassMethodEditor new - class: newClass; - expandAll) + SBToggledCode comment: '' active: 1 do: { + [newClass methodsDo: [:m | anEditor open: m]]. + [ + anEditor open: (SBStClassMethodEditor new + class: newClass; + expandAll)]} ] { #category : #progress } @@ -250,8 +253,9 @@ SBTutorialStep class >> stepFinal: anEditor [ SBTutorialStep new title: 'Congratulations!'; addIntroText: 'You finished the tutorial, well done! Press the button below to create a copy of an example class where you can freely experiment with the shortcuts or jump into any existing Smalltalk method using <#openOpenWindow> (then use to directly jump to a class).'; - addMorphBack: (SBButton new icon: SBIcon iconFlask label: 'Open Example Class' do: [self copyClass: SBObservableExample in: anEditor]); - addMorphBack: (SBButton new icon: SBIcon iconFlask label: 'Open Example Methods' do: [self copyClass: SBSortExample in: anEditor]); + addMorphBack: (SBButton new icon: SBIcon iconFlask label: 'Open Example Morph Class' do: [ + anEditor togglePalette. + self copyClass: SBExampleMorph in: anEditor]); setup: anEditor do: [:editor | ] ] @@ -303,6 +307,45 @@ SBTutorialStep class >> stepLists: anEditor [ title: 'Sequences'; addIntroText: 'Many elements act as sequences, for example arrays, statements in blocks, or even message sends. +By moving your cursor horizontally, you will pass by all the positions where elements can be inserted.'; + addAction: #insertElementAfter; + addAction: #insertElementBefore; + addAction: #insertStatementAbove; + addAction: #insertStatementBelow; + setup: anEditor do: [:step | | method | + method := step + createStepMethod: [ + {3. 4. 5}. + []. + 5 between: 2 + 2] + in: anEditor. + step + addStep: 'Add a 1 at the start of the array by moving the cursor just before it. Sandblocks will display a popup telling you that you are at an insert position. Then just hit the 1 on your keyboard.' + checkCondition: [:editor | method statements first firstSubmorph contents = '1']. + step + addStep: 'To insert an element after the 1, move the cursor out of the 1 and type 2.' + checkCondition: [:editor | method statements first submorphs second contents = '2']. + step + addStep: 'For sequences of statements, there is a special type of insert. Create an empty statement above the array using <#insertStatementAbove>. This works from any block independent of its nesting.' + checkCondition: [:editor | method statements size > 2 and: [method statements second isArrayBlock]]. + "step + addStep: 'Create an empty statement below the array by first moving the cursor to any number in the array and pressing <#insertStatementBelow>.' + checkCondition: [:editor | method statements size > 3 and: [method statements second isArrayBlock]]." + step + addStep: 'If you have an empty sequence, you insert an empty element by moving the cursor inside via <#moveCursorLeft> and <#moveCursorRight>. Move the insert cursor into the empty block near the end of the method and insert a `6 squared` statement into it.' + checkCondition: [:editor | (method statements detect: #isBlockBody) statements size > 0]. + step + addStep: 'Message sends also act like sequences: each message part with its argument is an element. Complete the expression in the last line by moving the insert cursor until the popup tells you that you are modifying the `between:` message send, then insert an element by typing `and: 6` to form a `between:and:` call.' + checkCondition: [:editor | method statements last prettySourceString = '5 between: 2 + 2 and: 6']] +] + +{ #category : #steps } +SBTutorialStep class >> stepListsShortcuts: anEditor [ + + SBTutorialStep new + title: 'Sequences'; + addIntroText: 'Many elements act as sequences, for example arrays, statements in blocks, or even message sends. + <#insertElementBefore> will always insert an **empty** element before the current selection, <#insertElementAfter> after the current, similar to <#pasteBefore> and <#pasteAfter> that insert the **copied** element before and after the selection.'; addAction: #insertElementAfter; addAction: #insertElementBefore; @@ -378,7 +421,6 @@ SBTutorialStep class >> stepMultiSelection: anEditor [ addIntroText: 'You can multiselect elements with a dedicated selection mode. Many commands will then act on all elements at once.'; addAction: #wrapInDynamicArray; addAction: #unwrapList; - addAction: #appendElement; addAction: #promptAction; setup: anEditor do: [:step | | method | method := step @@ -400,10 +442,10 @@ SBTutorialStep class >> stepMultiSelection: anEditor [ addStep: 'Press <#wrapInDynamicArray> to wrap your multi selection in an array.' checkCondition: [:editor | method statements first isArrayBlock]. step - addStep: 'Add a 6 to the array by selecting the 5 and then pressing <#insertElementAfter> to insert an element after it.' + addStep: 'Add a 6 to the array by moving your cursor after the 5 and typing a 6.' checkCondition: [:editor | method statements first submorphCount = 4 and: [method statements first lastSubmorph contents = '6']]. step - addStep: 'Finally, you can unwrap the array again using the unwrapList command, which might not be bound (<#unwrapist>). If it is not bound, you can access it by selecting the array and pressing <#promptAction> or right clicking it to see all available commands and filter for unwrap.' + addStep: 'Finally, you can unwrap the array again using the unwrapList command, which might not be bound (<#unwrapList>). If it is not bound, you can access it by selecting the array and pressing <#promptAction> or right clicking it to see all available commands and filter for unwrap.' checkCondition: [:editor | method statements size = 4]] ] @@ -423,10 +465,10 @@ SBTutorialStep class >> stepRestructuring: anEditor [ ({1. 4. 9. 16} includes: number) ifTrue: [^ OrderedCollection with: number sqrt + 1]] in: anEditor. step - addStep: 'First, let''s add an ifFalse: branch. We want to insert it in the message send''s sequence, so select the send''s last child, which is the block closure after the `ifTrue:`.' - checkCondition: [:editor | editor selection isBlockBody and: [editor selection isMethodBody not]]. + addStep: 'First, let''s add an ifFalse: branch. We want to insert it in the message send''s sequence, so move your cursor just after the block in the `ifTrue:`.' + checkCondition: [:editor | editor cursor mode = #insert and: [editor cursor cursorPosition container contents = 'ifTrue:' and: [editor cursor cursorPosition adjacent isBlockBody]]]. step - addStep: 'Press <#insertElementAfter> to now insert a new element in the sequence. Type `ifF`, use the autocompletion and then jump to the next hole using <#inputNextUnknown>.' + addStep: 'Type `iff`, use the autocompletion and then jump to the next hole using <#inputNextUnknown>.' checkCondition: [:editor | method statements last selector = 'ifTrue:ifFalse:']. step addStep: 'Usually, when you want to type an expression, you simply type it and the editor will restructure the tree for you. @@ -496,7 +538,7 @@ Note the yellow outline that indicates that a receiver will likely not understan Morph new] in: anEditor. step - addStep: 'Wrap the `Transcript showln: ''true` after the ifTrue: in a block by selecting the whole `Transcript showln:` message send via <#selectUp>, then press <#wrapInBlock>.' + addStep: 'Wrap the `Transcript showln: ''true` after the ifTrue: in a block by selecting the whole `Transcript showln:` message send via <#moveCursorLarger>, then press <#wrapInBlock>.' checkCondition: [:editor | method statements first arguments first isBlockBody]. step addStep: 'Next, wrap the 65 in an array by pressing <#wrapInDynamicArray>.'