[squeak-dev] Review Request: polish-file-dialogs.3.cs
christoph.thiede at student.hpi.uni-potsdam.de
christoph.thiede at student.hpi.uni-potsdam.de
Sat Jul 9 17:32:46 UTC 2022
Hi Marcel,
thanks for the feedback! Revised again and merged. :-)
> Browser>>updateCodePaneIfNeeded
> CodeHolder>>updateCodePaneIfNeeded
>
> Why is that in there?
Just a changeset slip, which I have resolved now.
Best,
Christoph
---
Sent from Squeak Inbox Talk
On 2022-07-07T10:57:30+02:00, marcel.taeumel at hpi.de wrote:
> Hi Christoph --
>
> Thanks! Looks good except maybe for:
>
> Browser>>updateCodePaneIfNeeded
> CodeHolder>>updateCodePaneIfNeeded
>
> Why is that in there?
>
> Maybe give your changes a fresh look with our own eyes again. And then improve the code with what you learned during the last 5 weeks. :-D
>
> Best,
> Marcel
> Am 20.05.2022 16:31:16 schrieb christoph.thiede at student.hpi.uni-potsdam.de <christoph.thiede at student.hpi.uni-potsdam.de>:
> Feel free to try this out already and give feedback, but given that this includes new features, it will not be merged before the next release. :-)
>
> Best,
> Christoph
>
> =============== Summary ===============
>
> Change Set: polish-file-dialogs
> Date: 20 May 2022
> Author: Christoph Thiede
>
> Cleans up, tests, and improves the convenience of file selection dialogs.
>
> UI improvements:
> * Add input field for file path to all dialogs. In file dialogs, a directory path can be entered to navigate to the relevant directory in the tree.
> * Update enablement of canAccept button based on input
> * Improve automatic selection of filenames
> * Use explicit help texts instead of filling the input field with a help message
> * Fixes handling of patterns/suffixes in save dialog
> * Double click a file/directory to choose it
> * Save dialog: Assure that the name of an existing directory cannot be chosen as a new file name
> * Small MVC improvements (however, modal invocation in MVC is still broken at the moment)
>
> Refactoring:
> * Overall deduplication
> * Consistent spelling of fileName (instead of filename)
>
> =============== Diff ===============
>
> Browser>>updateCodePaneIfNeeded {self-updating} · ct 5/20/2022 13:00
> + updateCodePaneIfNeeded
> +
> + super updateCodePaneIfNeeded.
> +
> + (self didCodeChangeElsewhere and: [self hasUnacceptedEdits not])
> + ifTrue:
> + [self setClassDefinition.
> + self contentsChanged].
>
> CodeHolder>>updateCodePaneIfNeeded {self-updating} · sw 2/14/2001 15:34 (changed)
> updateCodePaneIfNeeded
> "If the code for the currently selected method has changed underneath me, then update the contents of my code pane unless it holds unaccepted edits"
>
> self didCodeChangeElsewhere
> ifTrue:
> [self hasUnacceptedEdits
> ifFalse:
> [self setContentsToForceRefetch.
> self contentsChanged]
> ifTrue:
> [self changed: #codeChangedElsewhere]]
>
> DirectoryChooserDialog (source same but rev changed)
> FileAbstractSelectionDialog subclass: #DirectoryChooserDialog
> instanceVariableNames: ''
> classVariableNames: ''
> poolDictionaries: ''
> category: 'ToolBuilder-Morphic-Tools'
>
> DirectoryChooserDialog class
> instanceVariableNames: ''
>
> "A DirectoryChooserDialog is a modal dialog to allow choosing a directory. The actual directory chosen is returned, or nil if no selection was made.
>
> Normal usage would be
> myDirectory := DirectoryChooserDialog openOn: myApplicationDefaultDirectory label: 'Choose the directory to use'
> "
>
> DirectoryChooserDialog>>acceptDirectory: {directory tree} · ct 5/20/2022 12:11
> + acceptDirectory: dir
> +
> + self setDirectoryTo: dir.
> + self acceptFileName.
>
> DirectoryChooserDialog>>acceptFileName {accessing} · ct 5/20/2022 11:47 (changed and recategorized)
> acceptFileName
> "User clicked to accept the current state so save the directory and close the dialog"
>
> + self canAccept ifFalse: [^ false].
> finalChoice := directory.
> - self changed: #close
> + self changed: #close.
> + ^ true
>
> DirectoryChooserDialog>>buildDirectoryTreeWith: {toolbuilder} · ct 5/20/2022 12:10 (changed)
> buildDirectoryTreeWith: builder
>
> ^ (super buildDirectoryTreeWith: builder)
> hScrollBarPolicy: #never; "Use the dialog grips to see more"
> + doubleClick: #acceptDirectory:;
> yourself
>
> DirectoryChooserDialog>>buildWith: {toolbuilder} · ct 5/19/2022 18:10 (changed)
> buildWith: builder
> "assemble the spec for the chooser dialog UI"
>
> | windowSpec window |
> windowSpec := self buildWindowWith: builder specs: {
> - (self frameOffsetFromTop: 0
> + (self topConstantHeightFrame: self textViewHeight
> fromLeft: 0
> + width: 1) -> [self buildTextInputWith: builder].
> + (self frameOffsetFromTop: self textViewHeight
> + fromLeft: 0
> width: 1
> offsetFromBottom: 0) -> [self buildDirectoryTreeWith: builder].
> }.
> windowSpec buttons addAll: ( self buildButtonsWith: builder ).
> window := builder build: windowSpec.
> - window addKeyboardCaptureFilter: self.
> + (window respondsTo: #addKeyboardCaptureFilter: ) ifTrue: [
> + window addKeyboardCaptureFilter: self].
> self changed: #selectedPath.
> ^window
>
>
> DirectoryChooserDialog>>canAccept {accessing} · ct 5/19/2022 18:03
> + canAccept
> +
> + ^ directory notNil and: [directory exists]
>
> DirectoryChooserDialog>>inputText {filename} · ct 5/19/2022 17:40
> + inputText
> +
> + ^ directory fullName
>
> DirectoryChooserDialog>>inputText: {filename} · ct 5/20/2022 12:52
> + inputText: aText
> +
> + ^ self selectFileName: aText
>
> DirectoryChooserDialog>>selectFileName: {filename} · ct 5/20/2022 13:19
> + selectFileName: aStringOrText
> +
> + aStringOrText ifNil: [^ self].
> + self directory: ([FileDirectory on: aStringOrText asString] ifError: [^ self]).
> + isUpdating := true.
> + [self changed: #selectedPath]
> + ensure: [isUpdating := false].
> + self updateFileList.
> + self changed: #canAccept.
>
> DirectoryChooserDialogTest
> + nil subclass: #DirectoryChooserDialogTest
> + instanceVariableNames: ''
> + classVariableNames: ''
> + poolDictionaries: ''
> + category: 'MorphicTests-ToolBuilder'
> +
> + DirectoryChooserDialogTest class
> + instanceVariableNames: ''
> +
> + ""
>
> DirectoryChooserDialogTest>>expectedFailures {failures} · ct 5/19/2022 22:16
> + expectedFailures
> +
> + self flag: #todo. "Can only be debugged, but not run - this raises an InvalidDirectoryError which's defaultAction handles the exception silently. Should this class be a Notification instead?"
> + ^ #(testChooseAbsentDirectory testTypeAndChooseAbsentDirectory)
>
> DirectoryChooserDialogTest>>testChooseAbsentDirectory {tests - interface} · ct 5/20/2022 12:53
> + testChooseAbsentDirectory
> +
> + self openDialog.
> +
> + dialog acceptFileName: (self pathForFile: 'nurp').
> +
> + self assert: nil equals: result.
>
> DirectoryChooserDialogTest>>testChooseDefault {tests - interactions} · ct 5/19/2022 21:52
> + testChooseDefault
> +
> + self openDialog.
> +
> + self assert: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: mockDirectory equals: result.
>
> DirectoryChooserDialogTest>>testChooseDirectory {tests - interface} · ct 5/20/2022 12:53
> + testChooseDirectory
> +
> + self openDialog.
> +
> + dialog acceptFileName: mockChildDirectory fullName.
> +
> + self assert: mockChildDirectory equals: result.
>
> DirectoryChooserDialogTest>>testNewDirectory {tests - interface} · ct 5/19/2022 23:57
> + testNewDirectory
> +
> + self openDialog.
> +
> + [dialog newDirectoryName] valueSupplyingAnswer: #('*name*' 'nurp').
> + self assert: (mockDirectory directoryExists: 'nurp').
> + self assert: mockDirectory / 'nurp' equals: dialog directory.
> + dialog acceptFileName.
> +
> + self assert: mockDirectory / 'nurp' equals: result.
>
> DirectoryChooserDialogTest>>testSelectAndChooseDirectory {tests - interactions} · ct 5/19/2022 21:54
> + testSelectAndChooseDirectory
> +
> + self openDialog.
> +
> + dialog setDirectoryTo: (dialog subDirectoriesOf: mockDirectory) first.
> + self assert: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: mockChildDirectory equals: result.
>
> DirectoryChooserDialogTest>>testTypeAndChooseAbsentDirectory {tests - interactions} · ct 5/20/2022 12:52
> + testTypeAndChooseAbsentDirectory
> +
> + self openDialog.
> +
> + dialog selectFileName: (self pathForDirectory: mockDirectory file: 'twin').
> + self deny: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: nil equals: result.
>
> DirectoryChooserDialogTest>>testTypeAndChooseDirectory {tests - interactions} · ct 5/20/2022 12:53
> + testTypeAndChooseDirectory
> +
> + self openDialog.
> +
> + dialog selectFileName: mockChildDirectory fullName.
> + self assert: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: mockChildDirectory equals: result.
>
> FileAbstractSelectionDialog (changed)
> Model subclass: #FileAbstractSelectionDialog
> - instanceVariableNames: 'patternList directory directoryCache message listIndex fileName finalChoice nameList sizeList dateList suffixList'
> + instanceVariableNames: 'patternList directory directoryCache message listIndex fileName finalChoice nameList sizeList dateList suffixList isUpdating'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'ToolBuilder-Morphic-Tools'
>
> FileAbstractSelectionDialog class
> instanceVariableNames: ''
>
> "FileAbstractSelectionDialog is the abstract superclass for the file chooser & saver modal dialogs.
>
> The UI provides a message to the user, a text input field, a directory tree widget and a list of files within any chosen directory, and buttons to accept the selected file name/path or cancel the operation. See subclass comments and class side methods for specific usage examples.
>
> Instance Variables
> directory: <FileDirectory> used for the currently selected directory
> directoryCache: <WeakIdentityKeyDictionary> used to cache a boolean to help us more quickly populate the directory tree widget when revisiting a directory
> fileName: <String|nil> the name of the currently selected file, if any
> finalChoice: <String|nil> pathname of the finally chosen file, returned as the result of accepting; nil is returned otherwise
> list: <Array> the list of String of filenames (and date/size) that match the current pattern
> listIndex: <Integer> list index of the currently selected file
> patternList: <OrderedCollection of String> the patterns are held as a collection of string that may include * or # wildcards. See FileAbstractSelectionDialog>>#parsePatternString for details
> message: <String> a message to the user to explain what is expected
> nameList,DateList, sizeList: <Array> the list of file names matching the pattern and the appropriate date and size values, formatted for a PluggableMultiColumnListMorph"
>
> FileAbstractSelectionDialog class>>todo {documentation} · ct 5/20/2022 12:46
> + todo
> +
> + self flag: #forLater. "Possible future adventures for the file dialogs:
> +
> + * Allow users to enter patterns with stars to filter the current fileList (as known from Microsoft Windows file dialogs)
> + * Add support for multiple file selection
> + * Add MVC support (currently, modal dialog invocations windows seems not to work there). See also existing #mvc flag.
> + * Normalize paths with parentDirectoryNickname"
>
> FileAbstractSelectionDialog>>acceptFileName {accessing} · ct 5/20/2022 12:32 (changed and recategorized)
> acceptFileName
> - "User clicked to accept the current state so save the filename and close the dialog"
>
> - finalChoice := fileName.
> - self changed: #close
> + self canAccept ifFalse: [^ false].
> + self checkOrCorrectSuffix ifFalse: [^ false].
> + ^ self basicAcceptFileName
>
> FileAbstractSelectionDialog>>acceptFileName: {filename} · ct 5/20/2022 12:52
> + acceptFileName: aStringOrText
> +
> + self selectFileName: aStringOrText.
> + ^ self acceptFileName
>
> FileAbstractSelectionDialog>>basicAcceptFileName {accessing} · ct 5/20/2022 12:32
> + basicAcceptFileName
> + "Accept the file name without checking for patterns or suffices."
> +
> + self canAccept ifFalse: [^ false].
> + finalChoice := fileName.
> + self changed: #close.
> + ^ true
>
> FileAbstractSelectionDialog>>basicAcceptFileName: {filename} · ct 5/20/2022 12:54
> + basicAcceptFileName: aStringOrText
> + "Allow the user to press Cmd + S instead of enter to enforce a file name that does not match the pattern/suffx requirements."
> +
> + self selectFileName: aStringOrText.
> + ^ self basicAcceptFileName
>
> FileAbstractSelectionDialog>>buildButtonsWith: {toolbuilder} · ct 5/19/2022 14:06 (changed)
> buildButtonsWith: builder
>
> ^ {
> builder pluggableButtonSpec new
> model: self;
> label: 'Accept' translated;
> color: (self userInterfaceTheme get: #okColor for: #DialogWindow);
> - action: #acceptFileName.
> + action: #acceptFileName;
> + enabled: #canAccept;
> + yourself.
> builder pluggableButtonSpec new
> model: self;
> label: 'Cancel' translated;
> color: (self userInterfaceTheme get: #cancelColor for: #DialogWindow);
> - action: #cancelFileChooser}
> + action: #cancelFileChooser;
> + yourself}
>
> FileAbstractSelectionDialog>>buildDirectoryTreeWith: {toolbuilder} · mt 2/10/2022 10:13 (changed)
> buildDirectoryTreeWith: builder
> | treeSpec |
> treeSpec := builder pluggableTreeSpec new.
> treeSpec
> model: self ;
> roots: #rootDirectoryList ;
> hasChildren: #hasMoreDirectories: ;
> getChildren: #subDirectoriesOf: ;
> getSelectedPath: #selectedPath ;
> setSelected: #setDirectoryTo: ;
> getSelected: #directory;
> label: #directoryNameOf: ;
> menu: nil ;
> autoDeselect: false .
> ^ treeSpec
>
> FileAbstractSelectionDialog>>buildFileListWith: {toolbuilder} · ct 5/20/2022 12:08 (changed)
> - buildFileListWith: builder
> + buildFileListWith: builder
> +
> | listSpec |
> listSpec := builder pluggableListSpec new.
> listSpec
> - model: self ;
> - list: #fileList ;
> - getIndex: #fileListIndex ;
> - setIndex: #fileListIndex: ;
> - menu: nil ;
> - keyPress: nil ;
> - frame:
> - (self
> - frameOffsetFromTop:0
> - fromLeft: 0
> - width: 1
> - bottomFraction: 1) .
> - ^listSpec
> + model: self;
> + list: #fileList;
> + getIndex: #fileListIndex;
> + setIndex: #fileListIndex:;
> + doubleClick: #acceptFileName;
> + keyPress: nil;
> + frame:
> + (self
> + frameOffsetFromTop:0
> + fromLeft: 0
> + width: 1
> + bottomFraction: 1).
> + ^ listSpec
>
> FileAbstractSelectionDialog>>buildTextInputWith: {toolbuilder} · ct 5/20/2022 13:04 (changed)
> buildTextInputWith: builder
> | textSpec |
> textSpec := builder pluggableInputFieldSpec new.
> textSpec
> model: self;
> name: #inputText ;
> - font: self textViewFont;
> getText: #inputText;
> - setText: #selectFilename:;
> - selection: #contentsSelection.
> - ^textSpec
> + editText: #selectFileName:;
> + setText: #basicAcceptFileName:;
> + selection: #contentsSelection;
> + help: 'Enter a filename here or choose from list' translated.
> + ^textSpec
>
>
> FileAbstractSelectionDialog>>buildWith: {toolbuilder} · ct 5/19/2022 23:58 (changed)
> buildWith: builder
> - "assemble the spec for the common chooser/saver dialog UI"
>
> - ^self subclassResponsibility
> + | windowSpec window |
> + windowSpec := self buildWindowWith: builder specs: {
> + (self topConstantHeightFrame: self textViewHeight
> + fromLeft: 0
> + width: 1) -> [self buildTextInputWith: builder].
> + (self frameOffsetFromTop: self textViewHeight
> + fromLeft: 0.35
> + width: 0.65
> + offsetFromBottom: 0) -> [self buildFileListWith: builder].
> + (self frameOffsetFromTop: self textViewHeight
> + fromLeft: 0
> + width: 0.35
> + offsetFromBottom: 0) -> [self buildDirectoryTreeWith: builder].
> + }.
> + windowSpec buttons addAll: ( self buildButtonsWith: builder ).
> + window := builder build: windowSpec.
> + (window respondsTo: #addKeyboardCaptureFilter:) ifTrue: [
> + window addKeyboardCaptureFilter: self].
> + self changed: #selectedPath.
> + self inputText: fileName.
> + (window respondsTo: #positionOverWidgetNamed:) ifTrue: [
> + window positionOverWidgetNamed: #inputText].
> + ^window
>
> FileAbstractSelectionDialog>>canAccept {accessing} · ct 5/20/2022 11:53
> + canAccept
> +
> + ^ fileName isEmptyOrNil not
> + and: [self directory fileExists: fileName]
>
> FileAbstractSelectionDialog>>cancelFileChooser {accessing} · tpr 12/23/2017 12:35 (changed and recategorized)
> cancelFileChooser
> "User clicked to cancel the current state so nil the filename and close the dialog"
>
> directory := finalChoice := fileName := nil.
> self changed: #close.
>
> FileAbstractSelectionDialog>>checkOrCorrectSuffix {filename} · ct 5/20/2022 12:33
> + checkOrCorrectSuffix
> +
> + ^ patternList anySatisfy: [:each |
> + each match: fileName "caseSensitive: FileDirectory default isCaseSensitive"]
>
> FileAbstractSelectionDialog>>contentsSelection {toolbuilder} · ct 5/20/2022 13:18
> + contentsSelection
> + "Initial selection covers entire initial file name/path if any"
> +
> + ^ 1 to: (self inputText ifNil: [0] ifNotNil: #size)
>
> FileAbstractSelectionDialog>>directory {directory tree} · tpr 11/21/2017 09:22 (changed)
> directory
> "If nobody has set a specific directory we need a plausible default"
>
> ^ directory ifNil: [ directory := FileDirectory default]
>
> FileAbstractSelectionDialog>>directory: {directory tree} · tpr 11/20/2017 18:15 (changed)
> directory: aFileDirectory
> "Set the path of the directory to be displayed in the directory tree pane"
>
> directory := aFileDirectory
>
> FileAbstractSelectionDialog>>entriesMatching: {file list} · ct 5/20/2022 12:44 (changed)
> entriesMatching: patternList
> "Answer a list of directory entries which match any of the patterns.
> See #parsePatternString for the pattern rules"
>
> | entries |
> - "This odd clause helps supports MVC projects; the file list & directory views are built from a list that includes directories. In Morphic we filter out the directories because they are entirely handled by the direcctory tree morph"
> entries := Smalltalk isMorphic
> - ifTrue:[self directory fileEntries ]
> - ifFalse:[self directory entries].
> -
> + ifTrue: [self directory fileEntries]
> + ifFalse:
> + [self flag: #mvc. "This odd clause helps supports MVC projects; the file list & directory views are built from a list that includes directories. In Morphic we filter out the directories because they are entirely handled by the direcctory tree morph"
> + self directory entries copyWith:
> + (self directory entryAt: self directory class parentDirectoryNickname)].
> +
> (patternList anySatisfy: [:each | each = '*'])
> ifTrue: [^ entries].
>
> - ^ entries select: [:entry | patternList anySatisfy: [:each | each match: entry name]]
> + ^ entries select: [:entry |
> + patternList anySatisfy: [:each |
> + entry isDirectory or: [each match: entry name]]]
>
> FileAbstractSelectionDialog>>fileListIndex: {file list} · ct 5/20/2022 12:05 (changed)
> fileListIndex: anInteger
> "We've selected the file at the given index, so find the file name."
>
> self okToChange ifFalse: [^ self].
> listIndex := anInteger.
> - listIndex = 0
> - ifTrue: [fileName := nil]
> - ifFalse: [fileName := nameList at: anInteger]. "open the file selected"
> -
> + fileName := nameList at: anInteger ifAbsent: [nil].
> +
> + (fileName notNil and: [self directory directoryExists: fileName]) ifTrue:
> + [self flag: #mvc. "file list contains directories"
> + self setDirectoryTo: (self directory on: (self directory entryAt: fileName) fullName).
> + self changed: #selectedPath.
> + fileName := nil].
> +
> self
> changed: #fileListIndex;
> - changed: #inputText
> + changed: #inputText;
> + changed: #canAccept.
>
> FileAbstractSelectionDialog>>finalChoice {accessing} · tpr 12/23/2017 12:33 (changed and recategorized)
> finalChoice
> "return the chosen directory/filename that was saved by an accept click or nil; client must check for validity"
> ^ finalChoice
> ifNotNil: [self directory fullNameFor: finalChoice]
>
> FileAbstractSelectionDialog>>initialize {initialize-release} · ct 5/19/2022 17:55 (changed)
> initialize
> super initialize.
> directoryCache := WeakIdentityKeyDictionary new.
> listIndex := 0.
> patternList := self defaultPatternList.
> - suffixList := OrderedCollection new
> + suffixList := OrderedCollection new.
> + isUpdating := false.
>
> FileAbstractSelectionDialog>>listForPatterns: {path and pattern} · ct 5/19/2022 22:18 (changed)
> listForPatterns: arrayOfPatterns
> "build lists of name, date and size for those file names which match any of the patterns in the array.
> We use a Set to avoid duplicates and sort them by name"
>
> | newList |
> newList := Set new.
> newList addAll: (self entriesMatching: arrayOfPatterns).
>
> newList := newList sorted: [:a :b|
> a name <= b name].
> nameList := newList collect:[:e| e name].
> +
> + self flag: #dead. "dates and sizes are not in use"
> dateList := newList collect:[:e| ((Date fromSeconds: e modificationTime )
> printFormat: #(3 2 1 $. 1 1 2)) , ' ' ,
> (String streamContents: [:s |
> (Time fromSeconds: e modificationTime \\ 86400)
> print24: true on: s])].
> - sizeList := newList collect:[:e| e fileSize asStringWithCommas]
> + sizeList := newList collect:[:e| e fileSize asStringWithCommas].
>
>
> FileAbstractSelectionDialog>>newDirectoryName {directory tree} · ct 5/19/2022 21:43 (changed)
> newDirectoryName
> "Create a new directory; will be a subdirectory of the current chosen directory.
> If the user input is empty, or if the directory creation fails, fail this method.
> Update the directory tree display afterwards and set the current directory to the newly created directory"
> |userInput|
> - userInput := UIManager default request: 'New directory name' translated initialAnswer: 'newDir'.
> + userInput := Project uiManager request: 'New directory name' translated initialAnswer: 'newDir'.
> userInput isEmptyOrNil ifTrue: [^nil].
>
> [self directory createDirectory: userInput] ifError:[^nil]. "I hate using ifError: - it's so indiscriminate. Really ought to be a more precise error to catch properly"
> -
> +
> self changed: #rootDirectoryList.
> self directory: (self directory / userInput).
> self changed: #selectedPath
>
> FileAbstractSelectionDialog>>selectFileName: {filename} · ct 5/20/2022 12:52
> + selectFileName: aText
> +
> + | result |
> + fileName := aText asString.
> +
> + (directory class dirPathFor: aText asString) ifNotEmpty: [:otherDirPath |
> + ([directory on: otherDirPath] ifError: [nil]) ifNotNil: [:otherDir |
> + otherDir exists ifTrue:
> + [self setDirectoryTo: otherDir.
> + self changed: #selectedPath.
> + fileName := directory class localNameFor: aText asString.
> + self changed: #inputText.
> + self changed: #canAccept.
> + ^ self selectFileName: fileName]]].
> +
> + result := self selectExistingFileName.
> + self changed: #canAccept.
> + ^ result
>
> FileAbstractSelectionDialog>>setDirectoryTo: {directory tree} · ct 5/19/2022 23:54 (changed)
> setDirectoryTo: dir
> "Set the current directory shown in the FileList.
> Does not allow setting the directory to nil since this blows up in various places."
>
> dir ifNil:[^self].
> + dir isString ifTrue: [
> + self flag: #mvc. "PluggableListView auto-converts all items to strings :("
> + ^ self setDirectoryTo: (FileDirectory on: (Scanner new scanTokens: dir) third)].
> "okToChange is probably redundant.
> modelSleep/Wake is related to use of ServerDirectories, which are not yet hooked up"
> self okToChange ifFalse: [ ^ self ].
> self modelSleep.
> self directory: dir.
> self modelWakeUp.
> self changed: #directory.
> self updateFileList.
> - self changed: #inputText
> + isUpdating ifFalse: [self changed: #inputText].
>
> FileAbstractSelectionDialogTest
> + ClassTestCase subclass: #FileAbstractSelectionDialogTest
> + instanceVariableNames: 'mockDirectory mockFile1 mockFile2 mockFile3 mockChildDirectory mockChildFile dialog morph result'
> + classVariableNames: ''
> + poolDictionaries: ''
> + category: 'MorphicTests-ToolBuilder'
> +
> + FileAbstractSelectionDialogTest class
> + instanceVariableNames: ''
> +
> + ""
>
> FileAbstractSelectionDialogTest class>>isAbstract {testing } · ct 5/19/2022 20:57
> + isAbstract
> +
> + ^ self = FileAbstractSelectionDialogTest
>
> FileAbstractSelectionDialogTest>>classToBeTested {accessing} · ct 5/19/2022 20:20
> + classToBeTested
> +
> + ^ self dialogClass
>
> FileAbstractSelectionDialogTest>>createChildFile: {support} · ct 5/19/2022 21:34
> + createChildFile: fileName
> +
> + FileStream
> + fileNamed: (self pathForChildFile: fileName)
> + do: [:stream | stream nextPutAll: thisContext longPrintString].
>
> FileAbstractSelectionDialogTest>>createFile: {support} · ct 5/19/2022 21:34
> + createFile: fileName
> +
> + FileStream
> + fileNamed: (self pathForFile: fileName)
> + do: [:stream | stream nextPutAll: thisContext longPrintString].
>
> FileAbstractSelectionDialogTest>>dialogClass {accessing} · ct 5/19/2022 20:20
> + dialogClass
> +
> + ^ self targetClass
>
> FileAbstractSelectionDialogTest>>openDialog {support} · ct 5/19/2022 20:40
> + openDialog
> +
> + ^ morph := self toolBuilder build: dialog
>
> FileAbstractSelectionDialogTest>>pathForChildFile: {support} · ct 5/19/2022 21:31
> + pathForChildFile: fileName
> +
> + ^ self pathForDirectory: mockChildDirectory file: fileName
>
> FileAbstractSelectionDialogTest>>pathForDirectory:file: {support} · ct 5/19/2022 21:30
> + pathForDirectory: directory file: fileName
> +
> + ^ directory fullName, directory class slash, fileName
>
> FileAbstractSelectionDialogTest>>pathForFile: {support} · ct 5/19/2022 21:30
> + pathForFile: fileName
> +
> + ^ self pathForDirectory: mockDirectory file: fileName
>
> FileAbstractSelectionDialogTest>>setUp {running} · ct 5/19/2022 20:45
> + setUp
> +
> + super setUp.
> +
> + self setUpDirectory.
> + self setUpDialog.
>
> FileAbstractSelectionDialogTest>>setUpDialog {running} · ct 5/19/2022 20:42
> + setUpDialog
> +
> + dialog := self dialogClass new.
> + dialog addDependent: self.
> + dialog directory: mockDirectory.
>
> FileAbstractSelectionDialogTest>>setUpDirectory {running} · ct 5/19/2022 21:36
> + setUpDirectory
> +
> + mockDirectory := FileDirectory default / self class asString / UUID new asString.
> + mockDirectory assureExistence.
> +
> + {mockFile1 := 'plonk1.txt'.
> + mockFile2 := 'plonk2.st'.
> + mockFile3 := 'plonk3.cs'}
> + do: [:file | self createFile: file].
> +
> + mockChildDirectory := mockDirectory / 'child'.
> + mockChildDirectory assureExistence.
> +
> + mockChildFile := 'griffle.gif'.
> + self createChildFile: mockChildFile.
>
> FileAbstractSelectionDialogTest>>tearDown {running} · ct 5/20/2022 13:25
> + tearDown
> +
> + [mockDirectory ifNotNil: [mockDirectory assureAbsence].
> + (FileDirectory default / self class asString) assureAbsence]
> + ensure: [super tearDown].
>
> FileAbstractSelectionDialogTest>>testCancel {tests - interface} · ct 5/19/2022 22:17
> + testCancel
> +
> + self openDialog.
> +
> + dialog cancelFileChooser.
> +
> + self assert: nil equals: result.
>
> FileAbstractSelectionDialogTest>>testDirectoryTree {tests - interface} · ct 5/19/2022 22:20
> + testDirectoryTree
> +
> + | node index |
> + self openDialog.
> +
> + node := dialog rootDirectoryList
> + detect: [:root | (dialog directoryNameOf: root) = mockDirectory pathParts first]
> + ifNone: [].
> + self assert: node notNil.
> +
> + index := 2.
> + [self assert: (dialog hasMoreDirectories: node).
> + (dialog subDirectoriesOf: node)
> + detect: [:child | (dialog directoryNameOf: child) = (mockDirectory pathParts at: index)]
> + ifFound: [:child | node := child. index := index + 1]
> + ifNone: [self fail]]
> + doWhileFalse: [(dialog directoryNameOf: node) = mockDirectory localName].
> + self assert: mockDirectory equals: dialog directory.
> +
> + self deny: (dialog hasMoreDirectories: mockChildDirectory).
>
> FileAbstractSelectionDialogTest>>toolBuilder {accessing} · ct 5/19/2022 20:25
> + toolBuilder
> +
> + ^ self toolBuilderClass new
>
> FileAbstractSelectionDialogTest>>toolBuilderClass {accessing} · ct 5/19/2022 20:25
> + toolBuilderClass
> +
> + ^ MorphicToolBuilder
>
> FileAbstractSelectionDialogTest>>update: {updating} · ct 5/19/2022 20:41
> + update: aspect
> +
> + aspect = #close ifTrue:
> + [result := dialog finalChoice].
>
> FileChooserDialog>>inputText {filename} · ct 5/19/2022 14:02
> + inputText
> + "return the filename to appear in the text field"
> +
> + ^fileName
>
> FileChooserDialog>>inputText: {filename} · ct 5/20/2022 13:06
> + inputText: aText
> + "Initialize the filename entry field to aString. If a file with that name already exists, set up to highlight it."
> + aText ifNil: [^ self].
> + fileName := aText asString.
> + self selectExistingFileName
>
> FileChooserDialog>>selectExistingFileName {private} · ct 5/20/2022 13:06
> + selectExistingFileName
> + "Answer whether an existing file in the list matches my proposed filename, selecting it if it does."
> +
> + listIndex := nameList findFirst: [:each |
> + fileName isEmptyOrNil not and:
> + [each beginsWith: fileName "caseSensitive: FileDirectory default isCaseSensitive"]].
> + fileName := nameList at: listIndex ifAbsent: [nil].
> +
> + self changed: #fileListIndex.
> + self changed: #canAccept.
> +
> + ^ listIndex ~= 0
>
> FileChooserDialog>>userMessage {ui details} · ct 5/19/2022 17:40 (changed)
> userMessage
> - "return the string to present to the user in order to explain the purpose of this dialog appearing"
> + "return the string to present to the user in order to explain the purpose of this dialog appearing"
>
> ^message ifNil: ['Choose a file name' translated]
>
> FileChooserDialogTest
> + nil subclass: #FileChooserDialogTest
> + instanceVariableNames: ''
> + classVariableNames: ''
> + poolDictionaries: ''
> + category: 'MorphicTests-ToolBuilder'
> +
> + FileChooserDialogTest class
> + instanceVariableNames: ''
> +
> + ""
>
> FileChooserDialogTest>>testTypeToSelect {tests - interactions} · ct 5/20/2022 12:54
> + testTypeToSelect
> +
> + self openDialog.
> +
> + dialog selectFileName: mockFile2 allButLast.
> + self assert: mockFile2 equals: (dialog fileList at: dialog fileListIndex).
> + dialog acceptFileName.
> +
> + self assert: (self pathForFile: mockFile2) equals: result.
>
> FileDialogTest
> + nil subclass: #FileDialogTest
> + instanceVariableNames: ''
> + classVariableNames: ''
> + poolDictionaries: ''
> + category: 'MorphicTests-ToolBuilder'
> +
> + FileDialogTest class
> + instanceVariableNames: ''
> +
> + ""
>
> FileDialogTest class>>isAbstract {testing } · ct 5/19/2022 20:57
> + isAbstract
> +
> + ^ self = FileDialogTest
>
> FileDialogTest>>testChooseAbsentFile {tests - interface} · ct 5/20/2022 12:53
> + testChooseAbsentFile
> +
> + self openDialog.
> +
> + dialog acceptFileName: mockFile2 , '.absent'.
> +
> + self assert: nil equals: result.
>
> FileDialogTest>>testChooseFile {tests - interface} · ct 5/20/2022 12:53
> + testChooseFile
> +
> + self openDialog.
> +
> + dialog acceptFileName: mockFile2.
> +
> + self assert: (self pathForFile: mockFile2) equals: result.
>
> FileDialogTest>>testChooseNothing {tests - interactions} · ct 5/19/2022 21:03
> + testChooseNothing
> +
> + self openDialog.
> +
> + self deny: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: nil equals: result.
>
> FileDialogTest>>testFileList {tests - interface} · ct 5/19/2022 20:47
> + testFileList
> +
> + self openDialog.
> +
> + self assert: {mockFile1. mockFile2. mockFile3} equals: dialog fileList.
>
> FileDialogTest>>testFileListWithPattern {tests - interface} · ct 5/19/2022 20:47
> + testFileListWithPattern
> +
> + dialog pattern: '*2*'.
> + self openDialog.
> +
> + self assert: {mockFile2} equals: dialog fileList.
>
> FileDialogTest>>testFileListWithSuffixList {tests - interface} · ct 5/19/2022 20:49
> + testFileListWithSuffixList
> +
> + dialog suffixList: #('cs' 'st').
> + self openDialog.
> +
> + self assert: {mockFile2. mockFile3} equals: dialog fileList.
>
> FileDialogTest>>testSelectAndChooseFile {tests - interactions} · ct 5/19/2022 21:03
> + testSelectAndChooseFile
> +
> + self openDialog.
> +
> + dialog fileListIndex: (dialog fileList indexOf: mockFile2).
> + self assert: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: (self pathForFile: mockFile2) equals: result.
>
> FileDialogTest>>testSelectAndChooseSubFile {tests - interactions} · ct 5/19/2022 21:35
> + testSelectAndChooseSubFile
> +
> + self openDialog.
> +
> + dialog setDirectoryTo: (dialog subDirectoriesOf: dialog directory) first.
> + self assert: {mockChildFile} equals: dialog fileList.
> + dialog fileListIndex: (dialog fileList indexOf: mockChildFile).
> + self assert: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: (self pathForChildFile: mockChildFile) equals: result.
>
> FileDialogTest>>testTypeAndChooseAbsentFile {tests - interactions} · ct 5/20/2022 12:54
> + testTypeAndChooseAbsentFile
> +
> + self openDialog.
> +
> + dialog selectFileName: mockFile2 , '.absent'.
> + self deny: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: nil equals: result.
>
> FileDialogTest>>testTypeAndChooseFile {tests - interactions} · ct 5/20/2022 12:55
> + testTypeAndChooseFile
> +
> + self openDialog.
> +
> + dialog selectFileName: mockFile2.
> + self assert: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: (self pathForFile: mockFile2) equals: result.
>
> FileDialogTest>>testTypeAndChooseFullPath {tests - interactions} · ct 5/20/2022 12:55
> + testTypeAndChooseFullPath
> +
> + self openDialog.
> +
> + dialog selectFileName: (self pathForChildFile: mockChildFile).
> + self assert: mockChildDirectory equals: dialog directory.
> + self assert: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: (self pathForChildFile: mockChildFile) equals: result.
>
> FileSaverDialog>>buildButtonsWith: {toolbuilder} · ct 5/19/2022 23:59 (changed)
> buildButtonsWith: builder
> - "add a 'new directory' button to the beginning of the row of buttons"
> - ^{ builder pluggableButtonSpec new
> +
> + ^ (super buildButtonsWith: builder)
> + copyWith:
> + (builder pluggableButtonSpec new
> model: self;
> label: 'New Directory' translated;
> color: (self userInterfaceTheme get: #buttonColor for: #DialogWindow);
> - action: #newDirectoryName}, (super buildButtonsWith: builder)
> + action: #newDirectoryName;
> + yourself)
>
> FileSaverDialog>>canAccept {accessing} · ct 5/20/2022 11:55
> + canAccept
> +
> + ^ fileName isEmptyOrNil not
> + and: [(self directory directoryExists: fileName) not]
>
> FileSaverDialog>>checkOrCorrectSuffix {filename} · ct 5/20/2022 12:35
> + checkOrCorrectSuffix
> +
> + | suffix |
> + super checkOrCorrectSuffix ifTrue: [^ true].
> +
> + suffixList ifEmpty: [^ false].
> +
> + suffixList size = 1 ifTrue:
> + [((suffix := '.' , suffixList anyOne)
> + compare: (fileName last: (suffix size min: fileName size))
> + caseSensitive: directory isCaseSensitive)
> + = 2 ifFalse: [ fileName := fileName , suffix ].
> + ^ true].
> +
> + suffix := (Project uiManager
> + chooseFrom: suffixList
> + values: suffixList
> + title: 'Please choose the type of file to save.' translated)
> + ifNil: [^ false].
> + fileName := fileName , '.' , suffix.
> + self acceptFileName.
> + ^ true
>
> FileSaverDialog>>initialFilename: {accessing} · tpr 11/22/2017 16:39 (changed and recategorized)
> initialFilename: aFilenameOrNil
> "Set the initial choice of filename to highlight.
> We split the potential filename to see if it includes a path and if so, use that as the chosen directory - the client can manually change that with a subsequent send of #directory: if wanted.
> We split the root filename to find an extension and use that as the suffix - again, the client can manually change that later"
>
> | e f p |
> aFilenameOrNil ifNil:[^self].
>
> p := FileDirectory dirPathFor: aFilenameOrNil.
> p isEmpty ifFalse:[self directory: (FileDirectory on: p)].
> f := FileDirectory localNameFor: aFilenameOrNil.
> fileName := f.
> e := FileDirectory extensionFor: f.
> e isEmpty ifFalse:[self suffix: e]
>
> FileSaverDialog>>inputText {filename} · ct 5/19/2022 14:01 (changed)
> inputText
> "return the filename to appear in the text field"
>
> - ^fileName ifNil:['Enter a filename here or choose from list' translated]
> + ^fileName
>
> FileSaverDialog>>inputText: {filename} · ct 5/20/2022 13:06 (changed)
> inputText: aText
> "Initialize the filename entry field to aString. If a file with that name already exists, set up to highlight it."
> aText ifNil: [^ self].
> fileName := aText asString.
> - self selectExistingFilename
> + self selectExistingFileName
>
> FileSaverDialog>>selectExistingFileName {private} · ct 5/20/2022 13:06
> + selectExistingFileName
> + "Answer whether an existing file in the list matches my proposed filename, selecting it if it does."
> +
> + listIndex := nameList findFirst: [:each | each = fileName].
> + self changed: #fileListIndex.
> + ^ true
>
> FileSaverDialogTest
> + nil subclass: #FileSaverDialogTest
> + instanceVariableNames: ''
> + classVariableNames: ''
> + poolDictionaries: ''
> + category: 'MorphicTests-ToolBuilder'
> +
> + FileSaverDialogTest class
> + instanceVariableNames: ''
> +
> + ""
>
> FileSaverDialogTest>>testChooseAbsentFile {tests - interface} · ct 5/20/2022 12:53
> + testChooseAbsentFile
> +
> + self openDialog.
> +
> + dialog acceptFileName: mockFile2 , '.absent'.
> +
> + self assert: (self pathForFile: mockFile2 , '.absent') equals: result.
>
> FileSaverDialogTest>>testChooseAbsentFileWithSuffix {tests - interface} · ct 5/20/2022 12:53
> + testChooseAbsentFileWithSuffix
> +
> + dialog suffix: 'txt'.
> + self openDialog.
> +
> + dialog acceptFileName: mockFile2 , '.absent'.
> +
> + self assert: (self pathForFile: mockFile2 , '.absent.txt') equals: result.
>
> FileSaverDialogTest>>testChooseDirectory {tests - interface} · ct 5/20/2022 12:53
> + testChooseDirectory
> +
> + self openDialog.
> +
> + dialog acceptFileName: mockChildDirectory localName.
> +
> + self deny: dialog canAccept.
> + self assert: result isNil.
>
> FileSaverDialogTest>>testInitialFileName {tests - interface} · ct 5/19/2022 21:08
> + testInitialFileName
> +
> + dialog initialFilename: mockFile2.
> + self openDialog.
> +
> + self assert: mockFile2 equals: dialog inputText.
> + self assert: mockFile2 equals: (dialog fileList at: dialog fileListIndex).
> + self assert: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: (self pathForFile: mockFile2) equals: result.
>
> FileSaverDialogTest>>testNewDirectory {tests - interface} · ct 5/20/2022 12:53
> + testNewDirectory
> +
> + self openDialog.
> +
> + [dialog newDirectoryName] valueSupplyingAnswer: #('*name*' 'nurp').
> + self assert: (mockDirectory directoryExists: 'nurp').
> + self assert: mockDirectory / 'nurp' equals: dialog directory.
> + dialog acceptFileName: 'zonk'.
> +
> + self assert: (self pathForDirectory: mockDirectory / 'nurp' file: 'zonk') equals: result.
>
> FileSaverDialogTest>>testTypeAndChooseAbsentFile {tests - interactions} · ct 5/20/2022 12:55
> + testTypeAndChooseAbsentFile
> +
> + self openDialog.
> +
> + dialog selectFileName: mockFile2 , '.absent'.
> + self assert: dialog canAccept.
> + dialog acceptFileName.
> +
> + self assert: (self pathForFile: mockFile2 , '.absent') equals: result.
>
> FileSaverDialogTest>>testTypeAndChooseAbsentFileWithSuffixList {tests - interactions} · ct 5/20/2022 12:55
> + testTypeAndChooseAbsentFileWithSuffixList
> +
> + dialog suffixList: #('txt' 'cs').
> + self openDialog.
> +
> + "suffix choice is cancellable and dialog will remain open"
> + 2 timesRepeat:
> + [[dialog selectFileName: mockFile2 , '.absent'.
> + dialog acceptFileName]
> + valueSupplyingAnswer: #('*type*' cancel)].
> +
> + [dialog selectFileName: mockFile2 , '.absent'.
> + dialog acceptFileName]
> + valueSupplyingAnswer: #('*type*' 'cs').
> +
> + self assert: (self pathForFile: mockFile2 , '.absent.cs') equals: result.
>
> ---
> Sent from Squeak Inbox Talk [https://github.com/hpi-swa-lab/squeak-inbox-talk]
> ["polish-file-dialogs.3.cs"]
> ["DirectoryChooserDialog.png"]
> ["FileChooserDialog.png"]
> -------------- next part --------------
> An HTML attachment was scrubbed...
> URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20220707/4d4c468a/attachment-0001.html>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20220709/f0fe226c/attachment-0001.html>
More information about the Squeak-dev
mailing list
|