How to add a bounding box to a composite shape in Roassal 3? - smalltalk

I'm trying to draw a bounding box around a group of shapes. I get everything in the scene, but I don't know how to make the bounding box and the text get correctly aligned:
c := RSCanvas new.
text := RSGroup new.
foo := RSLabel new text: 'foo'.
bar := RSLabel new text: 'bar'.
text add: foo; add: bar.
RSVerticalLineLayout on: text.
bound := RSShapeFactory box
model: self;
border: (RSBorder new width: 1; color: Color black);
cornerRadius: 5;
width: text encompassingRectangle width + 15;
height: text encompassingRectangle height + 10.
all := RSComposite new shapes: { bound. text asShape }.
c add: all.
c # RSCanvasController.
^ c

So here is how I did it. The missing key point was to put an RSLocation.
c := RSCanvas new.
text := RSGroup new.
foo := RSLabel new text: 'foo'.
bar := RSLabel new text: 'bar'.
text add: foo; add: bar.
RSVerticalLineLayout on: text.
bound := RSShapeFactory box
model: self;
border: (RSBorder new width: 1; color: Color black);
cornerRadius: 5;
width: text encompassingRectangle width + 15;
height: text encompassingRectangle height + 10.
contents := text asShape.
all := RSComposite new shapes: { bound. contents }.
RSLocation new center; outer; stick: contents on: bound.
c add: all.
c # RSCanvasController.
^ c

here is another solution
text := 'Foo
bar'.
label := RSMultilineLabelBuilder new shapeFor: text.
box := RSBox new
fromRectangle: label encompassingRectangle;
cornerRadius: 10;
noPaint
withBorder.
box extent: box extent + 15.
all := { box . label} asGroup asShape.
canvas := RSCanvas new.
canvas add: all.
canvas # RSCanvasController
Maybe in a future we can add an extension method for strings 'hello world' asRoassalShape.

Related

Adding Text to a Smalltalk Cell

I'm doing a project in which I am adapting the Lights Out program in Pharo to a Minesweeper program, but I can't figure out how to add text to the cells so it shows up on-click like the color change does when it's "turned on". I've looked everywhere for a method for it with no dice.
Initialization method:
initialize
super initialize.
self label: ''.
self borderWidth: 4.
mineState := false.
cellValue := 0.
bounds := 0#0 corner: 32#32.
offColor := Color paleYellow.
onColor := Color paleBlue darker.
self useSquareCorners.
self turnOff
New Cell code:
newCellAt: i at: j
"Create a cell for position (i,j) and add it to my on-screen
representation at the appropriate screen position. Answer the new cell"
| c origin |
c := MFCell new.
"self labelString: 'hidden'."
origin := self innerBounds origin.
self addMorph: c.
c position: ((i - 1) * c width) # ((j - 1) * c height) + origin.
c mouseAction: [self checkMineAt: i at: j].
^ c
Maybe you are approaching your issue in too complex manner. You need to overload two selectors and you are done (not with the minesweeper, but the string on Cell).
I have taken the original code and just applied the label.
You have to just redefine turnOn and turnOff, which are in SimpleSwitchMorph which you are using, and apply a selector label: which can be found in super class SimpleButtonMorph. You can even have the same logic as the in the superclass.
In LOCell you can do (create a new protocol switching for them):
turnOn
super turnOn.
self label: 'X'
turnOff
super turnOff.
self label: ''
When running the example now, the X String will be added to the cells that are turned on.
It will look like this:
The protocol:
The morph now:

Add imagemorph to rectangle

I have a rectangle in smalltalk like this
cell := RectangleMorph new
extent: 70#70;
position: (500 + (aPositionWidth))#(100 + (aPositionHeight));
color: lastCellColor.
I'm trying to add an image to each rectangle like this:
queen := ImageReadWriter formFromFileNamed: '9813.gif'.
cell addMorph: queen.
queen position: cell position.
It's not working how can I add an image?
Thank you in advanced
ImageReadWriter class>>formFromFileNamed: returns a Form object. A form doesn't understand #position:. You need to convert it first to a morph. Try:
queen := (ImageReadWriter formFromFileNamed: 'queen.jpg') asMorph.

How to remove an attachMorph of HandleMorph in smalltalk from self

i need your help
i am creating a line from a spesific location to mouse location with this code.
after click i am trying to remove this line but i have no idea how
please help me remove the live after click what should i change ?
stk:= (LineMorph from: 100#100 to: 1300#1300 color: Color red width: 2) openInWorld.
handle := HandleMorph new forEachPointDo: [:newPoint | stk setVertices: {whiteBallinHole position. (newPoint-(10#10)). }.
stk on: #mouseDown send: #value: to:[:evt|
evt redButtonPressed ifTrue:[ self handlesMouseDown: evt.
"DELETE THE STICK AFTER MOUSE CLICK THIS DOSNT WORK PLEASE HELP"
stk color: Color transparent.
stk delete.
""
].
].
].
" (self currentHand attachMorph: handle)."
" self currentHand addMorph:handle. "
self currentHand attachMorph:handle.
The code is a bit of a mess. One thing that is out of place is
self handlesMouseDown: evt.
That is supposed to return true if you want to receive mouseDown: messages.
In a workspace, that self does not exist. And the result is never used. Just delete it. The resulting code would be something like
whiteBallinHole := CircleMorph new openInWorld .
stk := (LineMorph
from: 100#100 to: 1300#1300
color: Color red
width: 4) openInWorld.
handle := HandleMorph new forEachPointDo: [ :newPoint |
stk setVertices: {whiteBallinHole center. (newPoint-(10#10)). }.
stk on: #mouseDown send: #value: to: [:evt|
evt redButtonPressed ifTrue:[
stk color: Color transparent.
stk delete]]].
self currentHand attachMorph: handle.

How to get the height and width of the visible browser area

How can I retrieve the height and width of the browser area?
I want to show one form always in landscape mode, regardless whether the mobile phone has rotated the browser view.
if the browser is in portrait postion I want to rotate the form (TForm.Angel := 90). In order to force landscape mode.
Update:
Here is a picture, how the result should look like:
I found a solution, but I am not so really happy with it. It's easy to rotate the view, but the origin is not in the center, so I have to manually correct it, and I don't understand why this transformation is neccessary.
Here is the code:
procedure TForm1.ForceLandscape(aEnabled: Boolean);
var
browserWidth, browserHeight: Integer;
isLandscapeMode: Boolean;
begin
browserHeight := Application.Display.ClientHeight; //BrowserAPI.Window.innerHeight;
browserWidth := Application.Display.ClientWidth; //BrowserAPI.Window.innerWidth;
isLandscapeMode := browserHeight > browserWidth;
if aEnabled and isLandscapeMode then
begin
Angle := 90;
Height := browserWidth;
Width := browserHeight;
end
else
begin
Angle := 0;
Height := browserHeight;
Width := browserWidth;
end;
end;
procedure TForm1.InitializeForm;
begin
inherited;
// this is a good place to initialize components
//Need to put a transform.orign for form rotation (Angle)
var x := trunc(Application.Display.ClientWidth / 2);
var myStyle := TInteger.ToPxStr(x) + ' ' + TInteger.ToPxStr(x);
w3_setStyle(Handle, w3_CSSPrefix('TransformOrigin'), myStyle);
end;
The simplest way would be to use the main form's With and Height.
Create a new "Visual project" and add an EditBox to the form.
Put this code into the "Resize" method:
Edit1.Text := Format('%d x %d', [Self.Width, Self.Height]);
If you, however, want to keep the main-form at a fixed size, you would need to read some other properties.
Self.Width := 250;
Self.Height := 250;
There are several ways to get these dimensions. Both the "window" and "document" DOM-element have some properties you can use:
window.innerWidth
document.documentElement.clientWidth
document.body.clientWidth
In Smart you can access these from the Application object:
(Add two more edit-boxes...)
W3EditBox2.Text := Format('%d x %d', [Application.Document.ClientWidth, Application.Document.ClientHeight]);
W3EditBox3.Text := Format('%d x %d', [Application.Display.ClientWidth, Application.Display.ClientHeight]);
It seems to be a bug in Application.Document.ClientHeight as it always returns 0...
Remember that the running code is JavaScript. You can find lots of useful information on the web.
It's very easy to transform these JS-snippets into Smart Pascal via an asm section.
Eg:
var
w,h: Integer;
begin
//Pure JavaScript below.
//The #-prefix maps the variable to a SmartPascal declared variable
asm
#w = window.innerWidth;
#h = window.innerHeight;
end;
W3EditBox4.Text := Format('%d x %d', [w, h]);
end;
Another trick would be to declare a Variant variable.
A Variant variable can hold a whole JavaScript object, and enable "late binding"-ish access to the members of the object:
var
v: Variant;
begin
//Grab the whole "document" element from the DOM
asm
#v = document;
end;
//Access all "document" members directly via the "v"-variable
W3EditBox5.Text := Format('%d x %d', [v.documentElement.clientWidth, v.documentElement.clientHeight]);
end;
Screenshot of the code-snippets above:

Morphic: Automatically resize contained group box in a scroll pane?

Testing with Pharo 1.4 summer. I think is better to explain it using screenshots, code is below.
This is my initial state (what I'm getting now with my code):
I want the group box morph to be filled horizontally on opening, like this:
And after resizing, I want the group box morph to maintain the bounds, i.e.:
This is the code I've tried:
| s morph1 morph2 row groupBox scroller |
s := ScrollPane new.
morph1 := BorderedMorph new.
morph2 := BorderedMorph new.
row := UITheme builder newRow: {morph1 . morph2}.
groupBox := UITheme builder newGroupbox: 'Group Box Test' for: row.
groupBox
layoutFrame: (LayoutFrame fractions: (0 # 0 corner: 1 # 0.8));
vResizing: #spaceFill;
hResizing: #spaceFill;
layoutChanged.
scroller := s scroller.
scroller
addMorph: (groupBox position: 0#0)
fullFrame: (0 # 0 corner: 0.8#0.5).
s scroller color: Color white.
s
extent: 250#150;
openCenteredInWorld
The container must be a ScrollPane.
I have adapted the code of Bert to not to use the Polymorph UIBuilder, resulting in:
morph1 := Morph new.
morph2 := Morph new.
row := ScrollPane new.
row
color: Color white; borderColor: Color gray; borderWidth: 1;
layoutPolicy: ProportionalLayout new.
row scroller
addMorph: morph1 fullFrame: (LayoutFrame fractions: (0#0 corner: 1#0) offsets: (5#5 corner: -60#45));
addMorph: morph2 fullFrame: (LayoutFrame fractions: (1#0 corner: 1#0) offsets: (-50#5 corner: -5#45)).
row
extent: 250#150;
openInHand
but also doesn't work because the inner blue morphs are overlapped and not filling the specified frame. I tried many combinations using frames but nothing worked so far. Do you know what I'm missing?
Using UIBuilder you don't need to create manually a scroller. Check the following script by resizing until vertical or horizontal scroll bars appear automatically.
| dialog morphList row morph1 morph2 morph3 morph4 |
morph1 := BorderedMorph new hResizing: #spaceFill.
morph2 := BorderedMorph new.
morph3 := BorderedMorph new.
morph4 := BorderedMorph new.
row := UITheme builder newRow: {morph1 . morph2 . morph3}.
morphList := UITheme builder
newMorphListFor: (ListModel new list: {row . morph4})
list: #list
getSelected: #selectionIndex
setSelected: #selectionIndex:
help: 'This is a morph list'.
dialog := UITheme builder newWindowFor: nil title: 'titleString'.
dialog addMorph: morphList fullFrame: (LayoutFrame fractions: (0#0 corner: 1#0.5)).
dialog openInWorld
You're missing that ProportionalLayout does not use #spaceFill etc. So either use TableLayout:
morph1 := Morph new hResizing: #spaceFill.
morph2 := Morph new.
row := Morph new
color: Color white; borderColor: Color gray; borderWidth: 1;
layoutPolicy: TableLayout new;
layoutInset: 5; cellInset: 5;
listDirection: #leftToRight;
addAllMorphs: {morph1. morph2};
extent: 250#150;
openInHand
or a ProportionalLayout with offsets, which is how you can mix fixed-width and relative dimensions:
morph1 := Morph new.
morph2 := Morph new.
row := Morph new
color: Color white; borderColor: Color gray; borderWidth: 1;
layoutPolicy: ProportionalLayout new;
addMorph: morph1 fullFrame: (LayoutFrame fractions: (0#0 corner: 1#0) offsets: (5#5 corner: -60#45));
addMorph: morph2 fullFrame: (LayoutFrame fractions: (1#0 corner: 1#0) offsets: (-50#5 corner: -5#45));
extent: 250#150;
openInHand
I tried this in Squeak which does not have UITheme, so this is using Morphs directly.