REBOL 3 - How to update a layout that has already been viewed? - rebol

I'm trying to add a field to a layout after it has been viewed
view/no-wait m: [field "hello"]
insert tail m 'field
insert tail m "hello"
update-face m
** Script error: update-face does not allow block! for its face argument
I want to update the whole layout, not just the field or some part of it. If I try to use
view m, it opens a new window. Do I just have to un-view it and then view again?

You can use the LAYOUT function in R3-GUI as well. See the example below:
view/no-wait m: layout [field "hello"]
;We need to get the BACKDROP container which is first sub-face in the WINDOW face
m: first faces? m
append-content m [
field "world"
]
do-events
Ofcourse there are also other ways how to handle layout content dynamically.

Try this example from Richard
REBOL [
Title: "Layouts example #20"
Author: "Richard Smolak"
Version: "$Id: layouts-20.r3 852 2010-10-07 13:28:26Z cyphre $"
]
stylize [
tbox: hpanel [
about: "Simple rectangular box."
facets: [
init-hint: 200x200
min-hint: 0x0
max-hint: guie/max-pair
break-after: 1
]
options: [
init-hint: [pair!]
]
actors: [
on-make: [
append face/options [
content: [
button "hello" on-action [print "hello"]
button "world" on-action [print "hello"]
]
]
do-actor/style face 'on-make none 'hpanel
]
]
draw: [
pen red
fill-pen blue
box 0x0 (viewport-box/bottom-right - 1)
]
]
]
view [
test: tbox
button "clear"
on-action [
clear-content test
]
button "set"
on-action [
set-content test [
button "test"
field "the best"
]
]
button "insert"
on-action [
insert-content test bind/set probe reduce [to-set-word copy/part random "abcdefgh" 2 'button join "button #" 1 + length? test/gob] 'system
]
button "append"
on-action [
append-content test reduce ['button join "button #" 1 + length? test/gob]
]
button "remove 2 faces at pos 3"
on-action [
remove-content/pos/part test 3 2
]
]
so the words you're looking for are append-content and insert-content which take a face and a block as parameters where the block contains the definition of another face.

I don't know view yet, but I have a hint. The first line sets "m" to the block [field "hello"]. Check to see what "update-face" expects...

Related

How to add secondary text in elm?

I have a dropdown which shows text as moredetails with a keyboard-down-arrow, on clicking it expands and i can see the required fields. What i am trying to achieve is once on clicking the button when the secondary field expands i want to change the text to 'less details', i am able to achieve till like on expansion the keyboard downarrow mark becomes keyboard up arrow mark.
this is my code
Html.span
[ Attributes.class "details" ]
[ Html.a
[ Attributes.href "#", Events.onClick (ExpandDetails tx.id)]
[ Html.text "More detail"
, Html.i
[ Attributes.class "material-icons" ]
[ if expanded then
Html.text "keyboard_arrow_up"
else
Html.text "keyboard_arrow_down"
]
]
]
i am able to make it till if expands show downarrow but along with it i need text to change from more details to less details and on clicking less details it should be back to more details.
If I'm understanding correctly, you could define both the button and text in a let expression before the dropdown code. Something like:
let
( dropdownButton, dropdownText ) =
if expanded then
( "keyboard_arrow_up", "Less details" )
else
( "keyboard_arrow_down", "More details" )
in
Html.span [ Attributes.class "details" ]
[ Html.a
[ Attributes.href "#"
, Events.onClick (ExpandDetails tx.id)
]
[ Html.text dropdownText
, Html.i [ Attributes.class "material-icons" ]
[ Html.text dropdownButton ]
]
]
Here's an alternative.
Move the link into a function that takes the Strings as arguments.
viewDetails : Tx -> Bool -> Html Msg
viewDetails tx expanded =
let
lessOrMoreLink =
if expanded then
viewExpandLink "Less" "keyboard_arrow_up"
else
viewExpandLink "More" "keyboard_arrow_down"
in
Html.span
[ Attributes.class "details" ]
[ lessOrMoreLink tx.id ]
viewExpandLink : String -> String -> Int -> Html Msg
viewExpandLink txt arrow id =
Html.a
[ Attributes.href "#"
, Events.onClick (ExpandDetails id)
]
[ Html.text (txt ++ " details")
, Html.i
[ Attributes.class "material-icons" ]
[ Html.text arrow ]
]

Interesting way to catch all Rebol VID errors

I stumbled on this and just wanted to make sure this isn't a glitch in Rebol's design. I have the following code which seems to successfully catch all program errors in the VID environment.
view layout [
across
label "Rebol Command:"
f: field [
do f/text
focus f
] return
button "Error 1" [
print this-is-an-error-1
]
button "Error 2" [
print this-is-error-2
]
time-sensor: sensor 0x0 rate 1000
feel [
engage: func [face action event] [
if action = 'time [
time-sensor/rate: none
show face
if error? err: try [
do-events
true ; to make the try happy
][
the-error: disarm :err
? the-error
; reset sensor to fire again
time-sensor/rate: 1000
show face
focus f
]
]
]
]
do [
focus f
]
]
This isn't a glitch—do-events is indeed the dispatcher that will run until an error occurs. I'd suggest decoupling the error handler from the layout model itself though:
view/new layout [
across
label "Rebol Command:"
f: field [
do f/text
focus f
] return
button "Error 1" [
print this-is-an-error-1
]
button "Error 2" [
print this-is-error-2
]
do [
focus f
]
]
forever [
either error? err: try [
do-events
][
the-error: disarm :err
? the-error
focus f
][
break
]
]

request-date in a rebol2 vid application

Can someone tell me why the following code is not working? It is supposed to set the date in a field when it loads, then allow the date to be changed by clicking on the field. I'm using rebol/view 2.7.8 on linux. Actually, I think this code used to work years ago when I was using MS Windows, but not under linux for some reason.
drl
rebol []
trace true
out: layout [
style dater txt bold right [trans-date/date: copy (form now/date)] 48x24
dater "T-Date:" trans-date: field 80x24 (form now/date) feel [
engage: func [face action event][
if action = 'up [
lv-dat: request-date/date/offset (now/date) 450x375
if lv-dat <> none [
trans-date/text: form lv-dat
show trans-date
]
]
]
show trans-date
]
]
view out
Here is a cleaned up version of your code:
Rebol []
out: layout compose/deep [
style dater txt bold right 48x24
dater "T-Date:"
trans-date: field 80x24 (form now/date) feel [
engage: func [face action event][
if action = 'up [
lv-dat: request-date/date/offset (now/date) 450x375
if lv-dat [
face/text: form lv-dat
show face
]
]
]
]
]
view out
The main issue was the missing compose/deep call to evaluate the paren expressions before layout is called. However, that approach is not the usual way to initialize face properties, you should rather put init code in a do section of the VID block, like this:
Rebol []
out: layout [
style dater txt bold right 48x24
dater "T-Date:"
trans-date: field 80x24 feel [
engage: func [face action event][
if action = 'up [
lv-dat: request-date/date/offset now/date 450x375
if lv-dat [
face/text: form lv-dat
show face
]
]
]
]
do [trans-date/text: form now/date]
]
view out
Hope this helps.

Rebol text fields - checking values and changing colors

In the following prototype test code, I'm trying to create a comparison system that compares two fields, and colors them depending on whether they are equal or not.
comparecolors: [
either answer-user/text = answer-correct/text [
answer-user/font/color: green
answer-correct/font/color: green
show answer-user
show answer-correct
][
answer-user/font/color: red
answer-correct/font/color: black
show answer-user
show answer-correct
]
]
view layout [
answer: field [
answer-user/text: copy answer/text
do comparecolors
show answer
focus answer
show answer-user
]
label "Compare"
answer-user: info
answer-correct: info
across
text-list "Hello" "Goodbye" "Boy" "Girl" "Soldier" [
answer-correct/text: copy value
do comparecolors
show answer-correct
]
]
Some problems I am having:
The green color is affecting all the fields instead of just the ones I am specifying.
The red color is not working when the two fields are not equal.
The system does not check for none! value (I know it is not written so in the above code, but I tried some ways that didn't work, so I don't really know how to go about it).
Whenever you see multiple fields affected when you change the attribute of only one, it means that VID has made an optimization so that all those fields are sharing the same data structure, and in this the same font structure. So, we need to force VID to allocate a new font structure like this:
change-colors: func [ user [object!] correct [object!]
/local u c
][
set [ u c ]
either user/text = correct/text [
[ green green ]
][
[ red black ]
]
user/font/color: get u
correct/font/color: get c
show [ user correct ]
]
view layout [
answer: field [
answer-user/text: copy answer/text
change-colors answer-user answer-correct
focus answer
] font-color black
label "Compare"
answer-user: info font-color black
answer-correct: info font-color black
across
text-list "Hello" "Goodbye" "Boy" "Girl" "Soldier" [
answer-correct/text: copy value
change-colors answer-user answer-correct
]
]

A more elegant way to start a multithread alarm in Rebol VID ? (What's the equivalent of load event?)

I want to trigger an alarm when the GUI starts. I can't see what's the equivalent of load event of other language in Rebol VID, so I put it in the periodic handler which is quite circumvoluted. So how to do this more cleanly ?
alarm-data: none
set-alarm: func [
"Set alarm for future time."
seconds "Seconds from now to ring alarm."
message [string! unset!] "Message to print on alarm."
] [
alarm-data: reduce [now/time + seconds message]
]
ring: func [
"Action for when alarm comes due."
message [string! unset!]
] [
set-face monitor either message [message]["RIIIING!"]
; Your sound playing can also go here (my computer doesn't have speakers).
]
periodic: func [
"Called every second, checks alarms."
fact action event
] [
either alarm-data [
; Update alarm countdown.
set-face monitor rejoin [
"Alarm will ring in "
to integer! alarm-data/1 - now/time
" seconds."
]
; Check alarm.
if now/time > alarm-data/1 [
ring alarm-data/2
;alarm-data: none ; Reset once fired.
]
][
either value? 'message [
set-alarm seconds message
][
set-alarm seconds "Alarm triggered!"
]
]
]
alarm: func[seconds message [string! unset!]][
system/words/seconds: seconds
if value? 'message [
system/words/message: message
]
view layout [
monitor: text 256 ""
rate 1 feel [engage: :periodic]
button 256 "re/start countdown" [
either value? 'message [
set-alarm seconds message
][
set-alarm seconds "Alarm triggered!"
]
set-face monitor "Alarm set."
]
]
]
If the question is how to make something happen when the gui starts, you can do this
view layout [
text "Here's my layout"
do [
.... initialization code ...
]
]