Making GUI objects in Red language - user-interface

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 –

Related

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

How to expand math field in CKEditor

Math formula field is small.
This is default behavior of the Latex plugin. Can the field can be expanded? If yes, then how can I do that?
I have browse to fix this solution but none of them match with what I want. Anyone know how to expand it?
This is the screen shot:
UPDATED
return{title:b.title,minWidth:1000,minHeight:650,contents:[{id:"info",elements:[{id:"equation",type:"textarea",rows:10,label:b.dialogInput, ..
Note: I can expand the dialog display but I cannot expand the textfield formula form. It seem like the variable rows not functioning.
Should I declare the variable rows in other file?
You can edit the code that displays this dialog.
In your ckeditor folder, under the /plugins/mathjax/dialogs/ folder, open the mathjax.js file.
To make the dialog wider change the minWidth attributes. For example, to set the width to 750 pixels:
return {
title: lang.title,
minWidth: 750,
minHeight: 100,
contents: [
To add more lines to the formula textbox, add the rows attribute with the number of lines you want (the rows attribute is not there by default so add it). For example, to make the textbox 10 lines long:
elements: [
{
id: 'equation',
type: 'textarea',
rows: 10,
label: lang.dialogInput,
Here's a screenshot with the above changes:
Save the file and clear your cache before testing.

Refresh an image in Rebol

I've been working on this for a while. Gone through the REBOL docs and the answers here, but I am stumped. Can someone please tell me how to get an REBOL GUI image to update to another image? Here's the code I've gotten after two days of hacking at it. Any help would he appreciated.
REBOL [
Title: "Yvonne View"
]
yvonne: func[] [
parts: probe parse read http://pics.mytrapster.com/yvonne.php {="}
load to-string parts/6
]
img1: to-image (load-image yvonne)
img2: to-image (load-image yvonne)
v1: layout [
size 500x500
b: image img1
btn "Refresh" [ b: img2 ]
btn "Quit" [quit]
]
view v1
The url loads. The quit button works. The "b" variable just doesn't clear and update.
Thanks.
The way you can update an image is by using set-face
Change the refresh button line to:
btn "Refresh" [set-face b img2]
Alternatively if you are manually changing a pane of a face, you can use show (i.e. show b)

Multiple widget changes based on one button event in Kivy

I'm trying to build this application which contains multiple buttons. I can bind each button event to a callback but I am not able to change the state (namely the label) of any other button except for the one that fired the event. Does anyone know how to do this?
All you need is a reference to the other button, then you can do other_button.text = 'whatever'.
The way to do this depends on how you've constructed the program. For instance, if you constructed in the program in kv language, you can give your buttons ids with id: some_id and refer to them in the callback with stuff like on_press: some_id.do_something().
In pure python, you could keep references to the button in the parent class when you create them (e.g. self.button = Button()) so that the callback can reference self.button to change it. Obviously that's a trivial example, but the general idea lets you accomplish anything you want.
Probably not the official way, but try out the following code. It will change the text property of the buttons...
Ezs.kv file:
#:kivy 1.8.0
<Ezs>:
BoxLayout:
orientation: 'vertical'
padding: 0
spacing: 6
#choose
Button:
id: btn_1
text: 'text before'
on_press: btn_2.text = 'Whatever'
on_release: self.text = 'Who-Hoo'
#choose
Button:
id: btn_2
text: 'Press this'
on_release: self.text = 'HEEYY'
on_press: btn_1.text = 'text after'
.py file:
class Ezs(BoxLayout):
class EzsApp(App):
def build(self):
return Ezs
if __name__ == '__main__':
EzsApp().run()

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