Marcel Taeumel uploaded a new version of System to project The Trunk:
http://source.squeak.org/trunk/System-mt.856.mcz
==================== Summary ====================
Name: System-mt.856
Author: mt
Time: 1 August 2016, 3:17:05.483063 pm
UUID: e9f7d2ad-81a0-1d4f-b553-31510a255316
Ancestors: System-mt.855
Revise the system version descriptions. Be not so talky in release versions.
=============== Diff against System-mt.855 ===============
Item was changed:
----- Method: SystemVersion>>description (in category 'printing') -----
description
+ self isAlpha ifTrue: [^ 'ALPHA. New features which are not stable yet may come in\with each update. Also, existing features might not work reliably\due to updates and related changes.' translated withCRs].
- self isAlpha ifTrue: [^ 'This is an ALPHA version. It means that new features might be\introduced with each update or that very basic functions might\be quite buggy at the moment.\\Please refrain from using this version for important projects.' translated withCRs].
+ self isFeatureFreeze ifTrue: [^ 'FEATURE FREEZE. A new release is being prepared.\There will be only bugfixes, but no new features.' translated withCRs].
- self isFeatureFreeze ifTrue: [^ 'This is a FEATURE FREEZE version. We are in the middle of preparing\a new release. It means that there will be no new features but only bugfixes.\\Please do not contribute new features at the moment. ;-)' translated withCRs].
+ self isCodeFreeze ifTrue: [^ 'RELEASE CANDIDATE. The new release is almost ready.\There will be only bugfixes, if any.' translated withCRs].
- self isCodeFreeze ifTrue: [^ 'This is a RELEASE CANDIDATE version. It means that we are almost\done preparing the new release.\\Please do ONLY contribute URGENT fixes. Thank you. :-)' translated withCRs].
+ self isRelease ifTrue: [^ ''].!
- self isRelease ifTrue: [^ self inform: 'This is a release version. It means that there will be only\bugfixes but no new features.\\Have fun. :-)' translated withCRs].!
Marcel Taeumel uploaded a new version of Monticello to project The Trunk:
http://source.squeak.org/trunk/Monticello-mt.641.mcz
==================== Summary ====================
Name: Monticello-mt.641
Author: mt
Time: 1 August 2016, 3:12:22.642063 pm
UUID: 6e5db234-d1b0-1544-b633-dfd20aead82a
Ancestors: Monticello-mt.640
If we are in feature-freeze or code-freeze, inform the developer when committing to the trunk repository.
=============== Diff against Monticello-mt.640 ===============
Item was changed:
----- Method: MCWorkingCopyBrowser>>saveVersion (in category 'actions') -----
saveVersion
| repo |
self canSave ifFalse: [^self].
self checkForNewerVersions ifFalse: [^self].
repo := self repository.
+ (repo == MCRepository trunk and: [SystemVersion current isFeatureFreeze])
+ ifTrue: [self inform: 'FEATURE FREEZE. A new release is being prepared.\Please do only do bugfixes, but no new features.' translated withCRs].
+ (repo == MCRepository trunk and: [SystemVersion current isCodeFreeze])
+ ifTrue: [self inform: 'CODE FREEZE. The new release is almost ready.\Please do only do URGENT fixes, if any.' translated withCRs].
+
(self withRepository: repo do: [workingCopy newVersion]) ifNotNil:
[:v |
(MCVersionInspector new version: v) show.
Cursor wait showWhile: [repo storeVersion: v].
MCCacheRepository default cacheAllFileNamesDuring:
[repo cacheAllFileNamesDuring:
[v allAvailableDependenciesDo:
[:dep |
(repo includesVersionNamed: dep info name)
ifFalse: [repo storeVersion: dep]]]]]!
Marcel Taeumel uploaded a new version of ToolBuilder-Morphic to project The Trunk:
http://source.squeak.org/trunk/ToolBuilder-Morphic-mt.176.mcz
==================== Summary ====================
Name: ToolBuilder-Morphic-mt.176
Author: mt
Time: 1 August 2016, 2:44:38.851063 pm
UUID: 90acc5bd-1f6a-4f48-b1ac-d36a798437d4
Ancestors: ToolBuilder-Morphic-mt.175
For lists smaller than or equal to 7, use button-only dialog but still support keyboard filtering.
Note that people tend to choose between #(yes no) and hence we do need such a button-only version of a list chooser. Still, better than using menus in terms of input modality and visual consistency.
=============== Diff against ToolBuilder-Morphic-mt.175 ===============
Item was changed:
----- Method: MorphicUIManager>>chooseFrom:lines:title: (in category 'ui requests') -----
chooseFrom: aList lines: linesArray title: aString
"Choose an item from the given list. Answer the index of the selected item."
aList size <= 7 ifTrue: [
| dialog |
dialog := DialogWindow new
title: 'Please Choose';
message: aString;
+ filterEnabled: true;
yourself.
aList doWithIndex: [:ea :index |
dialog createButton: ea value: index].
dialog selectedButtonIndex: 1.
^ dialog getUserResponseAtHand ifNil: [0]].
^ ListChooser chooseFrom: aList title: aString!
Here is some explanation:
<http://forum.world.st/file/n4908989/list-chooser-squeak.png>
You can filter all these dialogs by just typing something and then pressing
<return> or <enter> or double-clicking some element in the list.
UIManager default chooseFrom: (Smalltalk classNames first: 7).
UIManager default chooseFrom: (Smalltalk classNames first: 8).
UIManager default chooseFrom: (Smalltalk classNames).
We need this button-only dialog because there is much code doing this:
UIManager default chooseFrom: #(yes no cancel).
<http://forum.world.st/file/n4908989/list-chooser-squeak-2.png>
Instead of asking for a confirmation dialog. I hope this works for
everybody. I terms of modality and visual consistency, I am strongly against
(mis)using pop-up menus for this choosing task in the future.
Best,
Marcel
--
View this message in context: http://forum.world.st/The-Trunk-ToolBuilder-Morphic-mt-176-mcz-tp4908988p49…
Sent from the Squeak - Dev mailing list archive at Nabble.com.
Levente Uzonyi uploaded a new version of Network to project The Trunk:
http://source.squeak.org/trunk/Network-ul.180.mcz
==================== Summary ====================
Name: Network-ul.180
Author: ul
Time: 25 July 2016, 8:40:01.001452 pm
UUID: 2f23a55c-fec5-41ac-95bd-6a8c2458be95
Ancestors: Network-nice.179
Socket changes:
- fixed the comment of #isOtherEndConnected and #isThisEndConnected
- do not slice the data (TCP) in the image in #sendData:. Let the VM, the kernel, the hardware deal with that.
- use #isOtherEndConnected when receiving data, and #isThisEndConnected when sending data instead of #isConnected
- move away from #milliseconds:since:, since we have a clock that won't roll over
=============== Diff against Network-nice.179 ===============
Item was changed:
----- Method: Socket>>closeAndDestroy: (in category 'connection open/close') -----
closeAndDestroy: timeoutSeconds
"First, try to close this connection gracefully. If the close attempt fails or times out, abort the connection. In either case, destroy the socket. Do nothing if the socket has already been destroyed (i.e., if its socketHandle is nil)."
+ socketHandle ifNil: [ ^self ].
+ self isThisEndConnected ifTrue: [
+ self close. "Close this end." ].
+ (self waitForDisconnectionFor: timeoutSeconds) ifFalse: [
+ "The other end has not closed the connect yet, so we will just abort it."
+ self primSocketAbortConnection: socketHandle ].
+ self destroy!
- socketHandle ifNotNil: [
- self isConnected ifTrue: [
- self close. "close this end"
- (self waitForDisconnectionFor: timeoutSeconds) ifFalse: [
- "The other end didn't close so we just abort the connection"
- self primSocketAbortConnection: socketHandle]].
- self destroy].
- !
Item was changed:
----- Method: Socket>>discardReceivedData (in category 'receiving') -----
discardReceivedData
"Discard any data received up until now, and return the number of bytes discarded."
| buf totalBytesDiscarded |
buf := String new: 10000.
totalBytesDiscarded := 0.
+ [self isOtherEndConnected and: [self dataAvailable]] whileTrue: [
- [self isConnected and: [self dataAvailable]] whileTrue: [
totalBytesDiscarded :=
totalBytesDiscarded + (self receiveDataInto: buf)].
^ totalBytesDiscarded
!
Item was changed:
----- Method: Socket>>isOtherEndConnected (in category 'queries') -----
isOtherEndConnected
+ "Return true if this socket is connected, or this end has closed the connection but not the other end, so we can still receive data."
- "Return true if this socket is connected, or this end has closed the connection but not the other end, so we can still send data."
| state |
socketHandle ifNil: [ ^false ].
(state := self primSocketConnectionStatus: socketHandle) == Connected ifTrue: [ ^true ].
^state == ThisEndClosed
!
Item was changed:
----- Method: Socket>>isThisEndConnected (in category 'queries') -----
isThisEndConnected
+ "Return true if this socket is connected, other the other end has closed the connection but not this end, so we can still send data."
- "Return true if this socket is connected, other the other end has closed the connection but not this end, so we can still receive data."
| state |
socketHandle ifNil: [ ^false ].
(state := self primSocketConnectionStatus: socketHandle) == Connected ifTrue: [ ^true ].
^state == OtherEndClosed
!
Item was changed:
----- Method: Socket>>sendData: (in category 'sending') -----
sendData: aStringOrByteArray
"Send all of the data in the given array, even if it requires multiple calls to send it all. Return the number of bytes sent."
"An experimental version use on slow lines: Longer timeout and smaller writes to try to avoid spurious timeouts."
| bytesSent bytesToSend count |
bytesToSend := aStringOrByteArray size.
bytesSent := 0.
[bytesSent < bytesToSend] whileTrue: [
(self waitForSendDoneFor: 60)
ifFalse: [ConnectionTimedOut signal: 'send data timeout; data not sent'].
count := self primSocket: socketHandle
sendData: aStringOrByteArray
startIndex: bytesSent + 1
+ count: bytesToSend - bytesSent.
- count: (bytesToSend - bytesSent min: DefaultSendBufferSize).
bytesSent := bytesSent + count].
^ bytesSent
!
Item was changed:
----- Method: Socket>>waitForConnectionFor:ifTimedOut:ifRefused: (in category 'waiting') -----
waitForConnectionFor: timeout ifTimedOut: timeoutBlock ifRefused: refusedBlock
"Wait up until the given deadline for a connection to be established. Return true if it is established by the deadline, false if not."
+ | deadline timeLeft status |
+ deadline := Time millisecondClockValue + (timeout * 1000) truncated.
+ (status := self primSocketConnectionStatus: socketHandle) == Connected ifTrue: [^true].
+ [ (status == WaitingForConnection) and: [ (timeLeft := deadline - Time millisecondClockValue) > 0 ] ]
- | startTime msecsDelta msecsEllapsed status |
- startTime := Time millisecondClockValue.
- msecsDelta := (timeout * 1000) truncated.
- status := self primSocketConnectionStatus: socketHandle.
- status = Connected ifTrue: [^true].
- [(status = WaitingForConnection) and: [(msecsEllapsed := Time millisecondsSince: startTime) < msecsDelta]]
whileTrue: [
+ semaphore waitTimeoutMSecs: timeLeft.
+ status := self primSocketConnectionStatus: socketHandle ].
+ status == Connected ifTrue: [ ^true ].
+ status == WaitingForConnection
+ ifTrue: [ timeoutBlock value ]
+ ifFalse: [ refusedBlock value ].
+ ^false!
- semaphore waitTimeoutMSecs: msecsDelta - msecsEllapsed.
- status := self primSocketConnectionStatus: socketHandle].
- status = Connected
- ifFalse: [
- status = WaitingForConnection
- ifTrue: [timeoutBlock value]
- ifFalse: [refusedBlock value].
- ^false].
- ^ true!
Item was changed:
----- Method: Socket>>waitForConnectionUntil: (in category 'waiting') -----
waitForConnectionUntil: deadline
"Wait up until the given deadline for a connection to be established. Return true if it is established by the deadline, false if not."
+ | status timeLeft |
- | status waitTime |
[
(status := self primSocketConnectionStatus: socketHandle) == Connected ifTrue: [ ^true ].
status == WaitingForConnection ifFalse: [ ^false ].
+ (timeLeft := deadline - Time millisecondClockValue) <= 0 ifTrue: [ ^false ].
+ semaphore waitTimeoutMSecs: timeLeft ] repeat!
- (waitTime := deadline - Time millisecondClockValue) > 0 ifFalse: [ ^false ].
- semaphore waitTimeoutMSecs: waitTime ] repeat!
Item was changed:
----- Method: Socket>>waitForDataFor:ifClosed:ifTimedOut: (in category 'waiting') -----
waitForDataFor: timeout ifClosed: closedBlock ifTimedOut: timedOutBlock
"Wait for the given nr of seconds for data to arrive."
+ | deadline timeLeft |
- | startTime msecsDelta |
socketHandle ifNil: [ ^closedBlock value ].
+ deadline := Time millisecondClockValue + (timeout * 1000) truncated.
- startTime := Time millisecondClockValue.
- msecsDelta := (timeout * 1000) truncated.
[
(self primSocketReceiveDataAvailable: socketHandle) ifTrue: [ ^self ].
+ self isOtherEndConnected ifFalse: [ ^closedBlock value ].
+ (timeLeft := deadline - Time millisecondClockValue) <= 0 ifTrue: [ ^timedOutBlock value ].
- self isConnected ifFalse: [ ^closedBlock value ].
- (Time millisecondsSince: startTime) < msecsDelta ifFalse: [ ^timedOutBlock value ].
"Providing a maximum for the time for waiting is a workaround for a VM bug which causes sockets waiting for data forever in some rare cases, because the semaphore doesn't get signaled. Remove the ""min: self class maximumReadSemaphoreWaitTimeout"" part when the bug is fixed."
readSemaphore waitTimeoutMSecs:
+ (timeLeft min: self class maximumReadSemaphoreWaitTimeout) ] repeat!
- (msecsDelta - (Time millisecondsSince: startTime) min: self class maximumReadSemaphoreWaitTimeout) ] repeat!
Item was changed:
----- Method: Socket>>waitForDataIfClosed: (in category 'waiting') -----
waitForDataIfClosed: closedBlock
"Wait indefinitely for data to arrive. This method will block until
data is available or the socket is closed."
socketHandle ifNil: [ ^closedBlock value ].
[
(self primSocketReceiveDataAvailable: socketHandle) ifTrue: [ ^self ].
+ self isOtherEndConnected ifFalse: [ ^closedBlock value ].
- self isConnected ifFalse: [ ^closedBlock value ].
"ul 8/13/2014 21:16
Providing a maximum for the time for waiting is a workaround for a VM bug which
causes sockets waiting for data forever in some rare cases, because the semaphore
doesn't get signaled. Replace the ""waitTimeoutMSecs: self class maximumReadSemaphoreWaitTimeout""
part with ""wait"" when the bug is fixed."
readSemaphore waitTimeoutMSecs: self class maximumReadSemaphoreWaitTimeout ] repeat!
Item was changed:
----- Method: Socket>>waitForDisconnectionFor: (in category 'waiting') -----
waitForDisconnectionFor: timeout
"Wait for the given nr of seconds for the connection to be broken.
Return true if it is broken by the deadline, false if not.
The client should know the connection is really going to be closed
(e.g., because he has called 'close' to send a close request to the other end)
before calling this method."
+ | deadline |
+ deadline := Time millisecondClockValue + (timeout * 1000) truncated.
+ [ self isOtherEndConnected and: [ deadline - Time millisecondClockValue > 0 ] ]
+ whileTrue: [
+ self discardReceivedData.
+ "Providing a maximum for the time for waiting is a workaround for a VM bug which causes sockets waiting for data forever in some rare cases, because the semaphore doesn't get signaled. Remove the ""min: self class maximumReadSemaphoreWaitTimeout"" part when the bug is fixed."
+ readSemaphore waitTimeoutMSecs:
+ (deadline - Time millisecondClockValue min: self class maximumReadSemaphoreWaitTimeout) ].
+ ^self isOtherEndConnected!
- | startTime msecsDelta status |
- startTime := Time millisecondClockValue.
- msecsDelta := (timeout * 1000) truncated.
- status := self primSocketConnectionStatus: socketHandle.
- [((status == Connected) or: [(status == ThisEndClosed)]) and:
- [(Time millisecondsSince: startTime) < msecsDelta]] whileTrue: [
- self discardReceivedData.
- "Providing a maximum for the time for waiting is a workaround for a VM bug which causes sockets waiting for data forever in some rare cases, because the semaphore doesn't get signaled. Remove the ""min: self class maximumReadSemaphoreWaitTimeout"" part when the bug is fixed."
- readSemaphore waitTimeoutMSecs:
- (msecsDelta - (Time millisecondsSince: startTime) min: self class maximumReadSemaphoreWaitTimeout).
- status := self primSocketConnectionStatus: socketHandle].
- ^ status ~= Connected!
Item was changed:
----- Method: Socket>>waitForSendDoneFor: (in category 'waiting') -----
waitForSendDoneFor: timeout
"Wait up until the given deadline for the current send operation to complete. Return true if it completes by the deadline, false if not."
+ | deadline timeleft |
+ deadline := Time millisecondClockValue + (timeout * 1000) truncated.
- | startTime msecsDelta msecsEllapsed |
- startTime := Time millisecondClockValue.
- msecsDelta := (timeout * 1000) truncated.
[
(self primSocketSendDone: socketHandle) ifTrue: [ ^true ].
+ self isThisEndConnected ifFalse: [ ^false ].
+ (timeleft := deadline - Time millisecondClockValue) <= 0 ifTrue: [ ^false ].
+ writeSemaphore waitTimeoutMSecs: timeleft ] repeat!
- self isConnected ifFalse: [ ^false ].
- (msecsEllapsed := Time millisecondsSince: startTime) < msecsDelta ifFalse: [ ^false ].
- writeSemaphore waitTimeoutMSecs: msecsDelta - msecsEllapsed ] repeat!
Hi, there! :-)
TL;DR: Here is a SAR file that cleans up the code of all widget classes such
as lists and buttons in your image and adds support for UI theming as well
as a bunch of UI themes (choose Themes via "Extras > Themes & Colors"):
squeak_trunk_uitheme_v12.sar
<http://forum.world.st/file/n4908439/squeak_trunk_uitheme_v12.sar>
Here is a preview:
http://i.giphy.com/l46CcX46yRE9H6yQ0.gif
*Try it out. Report bugs. Report performance issues!!* We will commit the
changes to trunk this Saturday.
Btw: The SAR installer is compatible with Eliot's current working image. ;o)
And there are numerous open windows and projects in that image...^^
==========================================
0. The Long History of Squeak & UI Theming
==========================================
...
http://forum.world.st/Dark-theme-td4869082.htmlhttp://forum.world.st/Themes-for-Squeak-5-1-td4882848.htmlhttp://forum.world.st/Skylark-Theme-and-Squeak-3-9-td54720.htmlhttp://forum.world.st/Non-SmallLand-colour-theme-lying-around-td4697438.htm…
...
========================
1. User Interface Themes
========================
Chris, Karl, and I designed a very lightweight abstraction to support UI
themes in Squeak. You can find the whole implementation in the class
UserInterfaceTheme and a bunch of tests in UserInterfaceThemeTests.
An object that wants to support theming SHOULD (!) implement three methods:
MyFancyClass class >> #themeProperties
MyFancyClass >> #applyUserInterfaceTheme
MyFancyClass >> #canApplyUserInterfaceTheme
It also MUST (!) access the current UI theme when setting visual state. And
here comes the nice part:
...
self color: (self userInterfaceTheme color ifNil: [Color white]).
...
Remember, usually you would write something like this:
...
self color: Color white.
...
How can this work? It works by using #doesNotUnderstand: and trigger a
dynamic lookup in a dictionary inside the current theme:
Object >> #userInterfaceTheme
^ UserInterfaceTheme current
pushScope: self;
yourself
UserInterfaceTheme >> #doesNotUnderstand: aMessage
...
^ [self get: scope top class -> aMessage selector]
ensure: [scope pop]
Isn't this slow? No, not at all. If you have any doubts, try benching it on
your machine:
UserInterfaceThemeTestObject benchLookup.
On my machine, this is even fast enough to support theme lookups in
frequently called messages such as Morph >> #drawOn:. However, applications
are advised to cache. That's why we have the explicit
#applyUserInterfaceTheme callbacks as described above.
Note that amazing effects can be achieved by adjusting #color, #borderColor,
#borderWidth, #fillStyle, and fonts. :-) The existing themes do exactly
that.
To get a feeling of the impact of having themes in Squeak, try browsing
senders and implementors of #applyUserInterfaceTheme, #themeProperties,
#userInterfaceTheme, and maybe #canApplyUserInterfaceTheme. I think we can
still reduce the number of sends to #userInterfaceTheme a little more.
==================================
2. Code clean-up in widget classes
==================================
We made Shout look up the current theme to build its internal cache of text
attributes to quickly style code as you type it.
We removed window color specifications and replaced them by simply
implementing Model >> #defaultWindowColor. There is also still the
#uniformWindowColor if you do not like colorful windows. This makes it also
much easier for new applications to have their own window color.
We unified the implementation of MenuMorph and DockingBarMorph and fixed
several bugs there.
Dialogs! *phew* ... *arrrrgss* ... Dialogs. :-) We managed to introduce a
general DialogWindow class. FillInTheBlankMorph and UserDialogBoxMorph are
now mere subclasses with a custom interface. There is also support for
ToolBuilder, see ListChooser for an example. *yay*
Buttons feel more like buttons now. It's a good thing, right?
For lists and trees and text boxes, we greatly reduced the amount of code
that just accessed global state. LazyListMorph, NewParagraph, TextMorph,
IndentingListItemMorph, ... they all now get configured by their "hosts",
which are PluggableListMorph, PluggableTextMorph, and
SimpleHierarchicalListMorph. Having this, we improved the modular structure
of our whole Squeak widget library. That modular structure made applying
themes much easier. :-)
========================
3. Theme all the widgets
========================
There is a nice existing pattern with the message #setDefaultParameters,
which is usually called from #initialize. Go, see for yourself. Browse
implementors of #setDefaultParameters. We used that pattern and cleaned up
many implementations of that message in the image.
Note that rounded corners, gradients, colorful windows, and shadows are
still preferences outside of UI themes. You can toggle them in any UI theme.
Note that the background color/image is also not subject to be themed. Just
drop your favorite picture or pattern in the image. It's really easy.
Now, take a look at the class SqueakTheme. There, you will find source code
for creating the default Squeak theme to be shared via SqueakSource and
reset during the release building process. :-)
Besides get/set/clear -- which might be expected from the theme structure
because it is basically a dictionary with some properties -- There, you will
notice a "merge" or "link" or "derive". When creating themes
programmatically, you can:
- derive properties from other properties
- link themes to extend property look-up
- merge one theme into another theme
How does property look-up work? Well, see UserInterfaceTheme >> #get:
1. Look up the key, which is usually "Class -> symbol".
2. Try the superclass chain.
3. Try the same lookup in the linked theme if any.
Theoretically, you could actually implement messages in subclasses of
UserInterfaceTheme and bypass that DNU-triggered lookup. We do not do this
right now.
As you can see, Squeak/Smalltalk offers a great deal of flexibility. It is
remains subject to discussion of how to employ the language and its
semantics. Think of the current implementation of user interface themes as
one way that represents a trade-off between a concise programming interface
(i.e. ... self userInterfaceTheme color ifNil ...), a fair amount of
comprehensive patterns (i.e. #setDefaultParameters,
#applyUserInterfaceTheme, ...), and some other existing constraints (i.e.
old code, Monticello/Squeaksource, existing tools such as
senders/implementors, ...).
Happy Squeaking!
Marcel, Chris, Karl
--
View this message in context: http://forum.world.st/ANN-Widget-Refactorings-UI-Themes-for-Squeak-tp490843…
Sent from the Squeak - Dev mailing list archive at Nabble.com.