Hide and unhide a view in Red language - window

I am trying following code to have a second view which can be hidden and shown again repeatedly while preserving values in its fields:
Red []
secondFstr: ""
secondshownonce: false
secondshowing: false
secondview: does [
secondshownonce: true
if not secondshowing [
secondshowing: true
view [ below
text "second view"
f2: field secondFstr []
b3: button "Hide" [
secondshowing: false
unview ]]]]
view [ below
text "first view"
b1: button "Print f2 text" [
either not secondshownonce
[print "not shown"]
[print f2/text] ]
b2: button "Show 2nd view" [secondview] ]
It works all right if 'hide' button is used for second view. But if the second view is closed by clicking 'x' at its top right corner, it cannot be shown again. How can I solve this problem?

You need to add an handler to the close event on that child window, in order to reset your flag properly, like this:
view/options [
below
text "second view"
f2: field secondFstr []
b3: button "Hide" [
secondshowing: false
unview
]
][
actors: object [
on-close: func [face event][
secondshowing: false
]
]
]

Related

Making 2 windows appear simultaneously in Red language

How can I make these 2 separate windows appear simultaneously and remain open thereafter unless the quit button is pressed?
Red []
view/flags [ below
text "second view"
f2: field "f2 text"
] 'no-buttons
view/flags [ below
text "first view"
b1: button "Print f2 text" [print f2/text]
quitb: button "quit" [quit]
] 'no-buttons
I tried to put second view in a function and call it from first view- but it shows only second view (first view is shown after second is closed by Alt-F4).
A call to view is a blocking call that will run its own event loop, until it is closed. view/no-wait will open the window without a new event loop, so it will not block (still requires an event loop to process events). So you can open the first window as non-blocking, and the last one in blocking mode, for running the event loop:
view/no-wait/flags [
below
text "second view"
f2: field "f2 text"
] 'no-buttons
view/flags [
below
text "first view"
b1: button "Print f2 text" [print f2/text]
quitb: button "quit" [quit]
] 'no-buttons

Creating gui objects in Red language

Is it possible to use make command to create gui object in Red/Rebol. I tried following:
view [
do [guiobj: make object! [
t: text "text"
f: field "fld"
b: button "button" ] ; end make object
obj1: make guiobj
obj2: make guiobj ] ; end do
below
obj1
obj2 ] ; end view
But I get following error:
*** Script Error: field has no value
*** Where: f
*** Stack: view layout do-safe
*** Script Error: VID - invalid syntax at: [obj1 obj2]
*** Where: do
*** Stack: view layout cause-error
I know compose can be used but can above code be made to work using make and object commands?
According to the documentation: Face objects are clones of face! template object.
It's possible to build up UIs without using VID—understanding that will help you understand how to manipulate the output from layout (and view). What you lose building things from scratch is the layout feature that VID offers, but we can get the best from both worlds. Let's have a go:
Window Without VID
First we'll need a place to put all our elements:
our-window: make face! [
type: 'window
text: "Our Window"
size: 500x500
]
Now let's stick some things in there:
our-window/pane: reduce [
make face! [
type: 'text
offset: 20x20
size: 160x28
text: "Text"
]
make face! [
type: 'field
offset: 200x20
size: 160x24
text: "Field"
]
make face! [
type: 'button
offset: 380x20
size: 160x28
text: "Button"
]
]
And now we can take a look at it:
view our-window
Note that the objects in our-window/pane are kind-of like the objects that would be generated in this example:
our-vid-window: layout [
text 160 "Text"
field 160 "Field"
button 160 "Button"
]
As I said, with this approach you have to manage sizes and offsets yourself. What we can do is generate our row, take those face objects and append it to our window.
Stealing Generated Faces from VID
Indeed we can actually create these objects with layout and drop them in our-window:
make-row: func [/local row face kid][
row: layout copy/deep [ ; copy so the strings are unique
text 160 "Text"
field 160 "Field"
button 160 "Button"
]
...
]
Using techniques from this answer you can even apply global words to each of these faces and will still work.
Before we do though, we're going to check if our-window has any children and adjust the offset of each of the new faces to appear below the last child:
if kid: last our-window/pane [
...
foreach face row/pane [
face/offset/y: face/offset/y + kid/offset/y + kid/size/y
]
]
To get the window sizing right, we're also going to adjust the generated row size and apply thus:
row/size/y: row/size/y + kid/offset/y + kid/size/y
...
our-window/size: row/size
And then the fun part:
append our-window/pane row/pane
Bringing this all together, we can generate a nicely sized window.
our-window: layout [
button "Add a Row" [make-row]
]
make-row: func [/local row face kid][
row: layout copy/deep [
text 160 "Text"
field 160 "Field"
button 160 "Button"
]
if kid: last our-window/pane [
row/size/y: row/size/y + kid/offset/y + kid/size/y
foreach face row/pane [
face/offset/y: face/offset/y + kid/offset/y + kid/size/y
]
]
our-window/size: row/size
append our-window/pane row/pane
]
make-row
make-row
make-row
view our-window

Making GUI objects in Red language

I have following simple code for a small panel:
view [
t: text "label"
f: field
button "Click here" [t/text: f/text] ]
But I have to make 2 of them and put them on one window. I want to create single object class and make 2 objects out of it. I see that objects can be created as follows:
obj: object [
view [
t: text "label"
f: field
button "Click here" [t/text: f/text] ] ]
view [
obj
obj ]
But I get following error:
*** Script Error: VID - invalid syntax at: [obj obj]
*** Where: do
*** Stack: view layout cause-error
How can this be done? Thanks for your help.
Edit: I tried with do but could manage only with does:
myview: object [
show: does [view[
below
t: text "1st time"
f: field "Enter value"
button "Click here" [f/text "clicked"]
area] ] ]
myview/show
print "READY TO SHOW 2nd OBJECT: "
myview2: copy myview
myview2/show
I want to create single object class and make 2 objects out of it.
There is no object class system in Red, so you should really try first to grasp the basic Red concepts before trying more complex GUI constructs. Red is a very flexible data-oriented language, so you can use that to your advantage by, for example, building parametrized block templates, and assembling them to form a correct block of VID code. Here is an example:
make-row: func [label [string!] but-label [string!]][
compose [
t: text (label)
f: field
b: button (but-label) [face/extra/1/text: face/extra/2/text]
do [b/extra: reduce [t f]]
]
]
view compose [
(make-row "label" "Click") return
(make-row "label2" "Click2")
]
Understanding the face tree (analog to the HTML DOM, just way simpler), is an important part of mastering Red's GUI system. As there is no much documentation yet (you can start with http://docs.red-lang.org), you are welcome to ask live questions on red/help Gitter room.
To work with objects instead of in the VID dialect, replace view with layout:
lay: layout [
t: text "label"
f: field
button "Click here" [t/text: f/text]
]
view lay
You can then inspect it like any other object: ?? lay.
For example, to access the contents of lay with pane:
>> ? lay/pane/1
However, a more useful function may be dump-face:
>> dump-face lay
Type: window Style: none Offset: 833x548 Size: 270x45 Text: "Red: untitled"
Type: text Style: text Offset: 10x10 Size: 80x24 Text: "label"
Type: field Style: field Offset: 100x10 Size: 80x24 Text: none
Type: button Style: button Offset: 190x9 Size: 70x25 Text: "Click here"
== make object! [
type: 'window
offset: 833x548
...
panels are useful to group objects together:
>> dump-face blay: layout [p: panel button "hi"]
Type: window Style: none Offset: none Size: 292x220 Text: none
Type: panel Style: panel Offset: 10x10 Size: 200x200 Text: none
Type: button Style: button Offset: 220x9 Size: 62x25 Text: "hi"
== make object! [
type: 'window
offset: none
...
But it's probably easier to use the VID dialect with compose to build up stuff first.
See also this question
I guess what you are looking for are styles and not objects in order to create a layout. Until now there is no official stylize function in Red. But you can create your layout dynamically like this
view repeat i 2 [
tname: to-word rejoin ['t i]
fname: to-word rejoin ['f i]
append v: [] compose/deep [
(to-set-word tname) text "label"
(to-set-word fname) field
button "click here" [
(to-set-path compose [(tname) text])
(to-path compose [(fname) text])
]
]
]
You can just append a predefined block of words many times to the block you want to view and you will get repeated elements.
txt_btn: [
t: text "label"
f: field
button "Click here" [t/text: f/text]
]
view append append [] txt_btn txt_btn
The problem arises as you refer to a named element in your block. But a word can not point to more than one element of the repeated elements, therefore the usage of compose in the complete solution in order to create unique names.
Maybe there is a bug in Red because I thought compose/deep would also do parentheses deep inside and not need more compose –

Comment out code by highlighting and simultaneously right clicking in Sublime Text

Rather than taking my hand of the mouse to use Ctrl + Shift + /, I'd like to be able to comment code by highlighting it, and then while keeping the left mouse button held down, simultaneously right click and have it comment out - though I don't know if that's possible with Sublime Text keybinds.
If not, I'll just use a custom keyboard shortcut, but I'd prefer the mouse method.
You can customize mousebinds by creating a file named Default (Windows).sublime-mousemap, Default (OSX).sublime-mousemap, Default (Linux).sublime-mousemap or (for any OS) Default.sublime-mousemap in your settings folder \Packages\User and then creating your custom bind inside that file
You can read more here (it's actually about keybinds but the process of creating mousebinds is the same.)
I came up with something similar to what you are trying to achieve but could not bind mouse1 as modifier so the snippet below is working different.
After you've made a selection, click and hold right mouse button and then click left mouse button to toggle comment block on selected area.
[
{
"button": "button1", "count": 1, "modifiers": ["button2"],
"command": "toggle_comment", "args": {"block": true},
"press_command": "drag_select_callback"
}
]
This might be annoying if you comment something by accident, so you can change click to double click, by changing count value to 2 or 3 for triple click etc.
Block comment is defined by {"block": true}, if you would like line comments just change it to false
[
{
"button": "button1", "count": 1, "modifiers": ["button2"],
"command": "toggle_comment", "args": {"block": false},
"press_command": "drag_select_callback"
}
]

Rebol VID MVC: how to update multiple views from model if Rebol doesn't support custom event?

Let's say I have a model/controller with 2 simultaneous VID forms. How will I update my 2 views pointing to the same model if Rebol doesn't support custom events ?
I went ahead and had fun implementing a property editor MVC.
this example allows you to create models and views dynamically directly from the GUI, so its pretty good at showing the system in action.
when multiple views edit the same data, you'll see them stay in sync. multiple models may have multiple views each.
This is just an example, showing how easy it is to build MVC patterns in REBOL. in deed, many constructs in REBOL are already MVC in spirit, even if they're not explicitly marketed as such.
rebol [
title: "MVC pattern example"
purpose: {
shows an example of a raw MVC pattern in REBOL
the views can create new models and new views, showing interaction
between separate models, views and the controler.
}
]
model!: context [
data: none
views: []
modify: func [label value][
set in data label value
]
propagate: func [
/only label
/local view
][
foreach view views [
either only [
view/refresh/only label
][
view/refresh
]
]
]
]
view!: context [
controller: none ; our controller
model: none ; our model
label: none ; what label in data does this view manipulate?
gui: [
across
space 2x10
style separator box 275x3 edge [size: 1x1 effect: 'ibevel color: (white * .75)]
]
lbl: none ; gui face
fld: none ; gui face
refresh: func [/only label][
; GENERATE the gui if its not been built for this view yet.
if block? gui [
gui: copy/deep gui
; add a button for each item of data in the model, clicking on them changes
; what the field edits.
foreach item words-of model/data [
append gui compose/deep bind/copy [
btn (to-string item) [
print "^/---"
label: (to-lit-word item)
probe label
refresh
]
] self
]
; we must bind because the block is being used in new objects created dynamically.
; if we don't bind the blocks, they stay bound to the class... important detail.
append gui copy/deep bind/copy [
return
separator
return
lbl: h1 200 (to-string label)
return
fld: field [controller/modify model label face/text]
btn "randomize" [controller/randomize model label]
return
pad 0x10
separator
return
pad 160x0
btn "new view" [controller/new-view (model)]
btn "new model" [controller/new-model]
btn "close" [unview/only gui]
] self
gui: view/new layout gui
]
; refresh the gui, when its already built (including on first view)
if any [
none? label
label = self/label
] [
probe model/data
probe self/label
fld/text: copy get in model/data self/label
lbl/text: copy to-string self/label
show fld
show lbl
]
]
]
controller!: context [
models: []
; this just describes how the models should be built,
; it could be a hard-coded in new-model()
model-data: [sid: "0" name: "unknown" occupation: "unknown"]
new-model: func [/local model view prev-model prev-view][
unless empty? models [prev-model: last models]
append models model: make model! [data: context model-data ]
view: new-view model
if prev-model [
; tweak window position which is a bit screwed up in rebol
prev-view: last prev-model/views
view/gui/offset/x: view/gui/offset/x + system/view/no-resize-border/x
view/gui/offset/y: prev-view/gui/offset/y + prev-view/gui/size/y + system/view/no-resize-border/y 8
show view/gui
]
model
]
new-view: func [model /local view prev-view][
probe model/data
if not empty? model/views [
probe length? model/views
prev-view: last model/views
]
append model/views view: make view! compose [
model: (model)
label: (to-lit-word first words-of model/data)
controller: (self) ; here self is the controller, since we are composing
; the value within a controller function
]
view/refresh
if prev-view [
; tweak window position which is a bit screwed up in rebol
view/gui/offset/x: prev-view/gui/offset/x + prev-view/gui/size/x + system/view/no-resize-border/x
view/gui/offset/y: prev-view/gui/offset/y - system/view/title-size/y - system/view/no-resize-border/y - 2
show view/gui
]
view
]
; general case "set" operation
modify: func [model label value][
model/modify label value
model/propagate/only label
]
; just an example controler method
randomize: func [
model
label
][
modify model label random copy get in model/data label
]
]
ids: make controller! []
ids/new-model
do-events

Resources