Can a program generate its own VID code

Discussion in 'Rebol' started by swhite, Aug 25, 2011.

  1. swhite

    swhite Member

    I want to write a program that will modify its own screen, depending on various conditions, before it displays it. For example:

    REBOL [
    ]

    WINDOW-CODE: [
    vh1 "Window heading"
    button "b1" [print "b1 pressed"]
    button "b2" [print "b2 pressed"]
    button "quit" [quit]
    ]

    view layout WINDOW-CODE

    The above screen has two buttons, hard-coded. But what I really want is b1 only, or maybe b2 only, or maybe both, depending on some condition evaluated at run time. I don't quite know how to go about that.

    I thought that I could build up the VID code in a string, then make it a block with to-block, then using layout on it, but that did not work.

    I tried making the whole VID code (in a different and more complicated example) in two blocks, a skeleton block with the unchanging part and another block with the variable part, and then joining them, and then using layout, but that did not work.

    I'm pretty sure this can be done, but I just don't quite see how to go about it.

    Thank you.
  2. MaxV

    MaxV Member

    There are many ways to do this, I'll show you a couple.
    The most simple is to build a layout with all components and show only some of them, depending of the condition.
    Code:
    window: layout [
        a: button "a" [ hide b show c ]
        b: button "b" [hide c show  a]
        c: button "c" [hide a show  b] with [show?: false]
         ]
    view window
    
    hide doesn't permit to hide the element that launch itself i.e you can't do:
    Code:
    a: button [hide a]
    but there is another way:
    Code:
    view layout [
        button "Click me to hide!" 120
        feel [engage: func [f a e][hide f ]]
    ]
    A little bit more complicate is rewrite entirely the layout block:
    Code:
    window: copy []
    clear_L: func [] [window: copy [] 
      a/text: " " show a  ]
    build_L: func [item] [ append window item  
      a/text: mold window 
      show a]
    view layout [
    button "button" [build_L 'button ]
    button "filed" [build_L 'field ]
    button "text" [build_L 'text
       build_L "Text example"  ]
    button red "Clear all" [clear_L]
    button green "test it" [test: layout window 
      view/new test]
    return
    a: area 
    ]
  3. swhite

    swhite Member

    How DO you LEARN this stuff? That was a nice little sample.

    It was not quite what I had in mind, but it got me thinging, and I have a solution.
    A code sample follows, though not the finished product.

    DIANA-WINDOW-SKELETON: {
    across
    image COB-LOGO
    vh1 "Document Index And Notepad Assistant"
    return
    DIANA-PHOTO: image 535x350 %pictures/mission.jpg effect [aspect]
    return
    button "Quit" [DIANA-QUIT-BUTTON]
    button "Help" [DIANA-HELP-BUTTON]
    return
    box brick 535x2
    return
    }
    DIANA-WINDOW-BODY: {
    button "Viewer" [DIANA-RUN-PROGRAM "viewer.r"]
    button "Textedit" [DIANA-RUN-PROGRAM "textedit.r"]
    button "Imagestring" [DIANA-RUN-PROGRAM "imagestring.r"]
    button "Wavplayer" [DIANA-RUN-PROGRAM "wavplayer.r"]
    button "Aixftp" [DIANA-RUN-PROGRAM "aixftp.r"]
    }

    DIANA-MAIN-WINDOW: load join "[" [
    DIANA-WINDOW-SKELETON
    DIANA-WINDOW-BODY
    "]"
    ]

    view layout DIANA-MAIN-WINDOW

    end of sample

    The part identified as DIANA-WINDOW-BODY is the variable part,
    a string I will build up at run time and that could be different with
    each running. Then I JOIN the WINDOW-SKELETON and WINDOW-BODY,
    along with appropriate opening and closing brackets, LOAD it, run it through
    the LAYOUT function, and then VIEW it. The "load" function was the
    missing piece for me.

    This ability to mix code and data has some very interesting possibilities.

    Thank you.
  4. MaxV

    MaxV Member

  5. henrikmk

    henrikmk New Member

    It is a good idea to learn the design of REBOL as this helps to learn how to be flexible with blocks, words and dialects.

    In the example, you are purely working with strings, which is, while possible, usually harder than simply manipulating blocks and this is one of the big strengths of REBOL.

    If you learn that, you can get really far.
  6. swhite

    swhite Member

    You have mentioned before the importance of having a deeper understanding of the design of REBOL. While I am in complete agreement, I don't quite see how to go about it. As for the example, I recall that I did try various things with blocks, and failed, and then used strings just to get the job done and move on. As an example:

    Using this script as a starting point...

    REBOL [
    Title: "Dynamic VID test"
    ]

    WINDOW-WHOLE: [
    vh1 "Window heading"
    button "Quit" [quit]
    button "Halt" [halt]
    button "Button 1" [alert "Button 1 pressed"]
    button "Button 2" [alert "Button 2 pressed"]
    ]

    view layout WINDOW-WHOLE

    ...I broke the window into pieces, something like this...

    REBOL [
    Title: "Dynamic VID test"
    ]

    WINDOW-HEAD: [
    vh1 "Window heading"
    button "Quit" [quit]
    button "Halt" [halt]
    ]

    WINDOW-BODY: [
    button "Button 1" [alert "Button 1 pressed"]
    button "Button 2" [alert "Button 2 pressed"]
    ]


    ;;WINDOW-WHOLE: join WINDOW-HEAD WINDOW-BODY ;; does not work
    WINDOW-WHOLE: rejoin [WINDOW-HEAD WINDOW-BODY] ;; shows WINDOW-HEAD only

    view layout WINDOW-WHOLE

    ...and couldn't make anything work. Since I did not have that deeper understanding of the design of REBOL, I experimented until I hit on the string approach, and used that.

    It's too bad Carl can't clone himself, one to write REBOL and one to explain it. I can imagine him looking at something I have written and saying something like, "NO, don't do it THAT way. It wasn't DESIGNED to be used like that. Don't you GET it?" Sadly, I don't. But I still am getting useful work done, and having fun too.
  7. Graham

    Graham Developer Staff Member

    You need to look at the error message and then you'll see what you did wrong.

    In this case 'join evaluates the block, but you can't evaluate a dialect since the words have no meaning except to the function that parses the dialect.

    You needed to do something like

    Code:
    view layout insert tail copy/deep window-head copy/deep window-body
    
    as 'insert will combine the two series without evaluating the contents.
  8. henrikmk

    henrikmk New Member

    The best way, I think, is to open a console and experiment with small blocks, to see which function works best for you. When you have found a good method, you can add it to the script, as passing an incorrectly formatted block to a dialect like VID can be hard to understand and debug.

    A quick way to solve the above problem could be by using APPEND. But here, it's valuable to be aware of how REBOL aggressively avoids copying data, which means that a block passed to a function may be modified. Therefore, COPY is important. So when using APPEND, it will modify the block that is passed to it.

    It will not modify the second block, because only its elements are inserted in the first block, not the whole block.

    Another detail about COPY is that it works on one "layer" of series. That means that if you have blocks (or other series) inside blocks

    Graham uses COPY/DEEP and that is useful if you want to be certain that no aspect of the original block will be modified. Technically, you can still not be certain about that, because objects are not copied using COPY. Here we don't use objects, so that's ok. In the example above, it should be enough with a single COPY and a tiny bit faster. You can find a way to balance it, as you learn when you need to copy and when not to. Balancing it will allow you to save memory and have your script run faster.

    Another method is COMPOSE:

    APPEND and COMPOSE, each have an /ONLY refinement to keep the passed block(s) rather than only using the elements of the block.

    About using strings: You can use strings, just like some other languages allow this, but one powerful aspect of REBOL is that code and data are nearly 100% interchangeable. You can write a piece of code and treat it as data, as a dialect, shuffle words around, and pass it somewhere else. The idea is more in the category of processing symbols, which, depending on use, can have a specific meaning. You will notice that the VID word AT is not the same as the REBOL function AT, but both are, when treated as data, the same words. You just use them differently in different situations.

    VID is a dialect, which the LAYOUT function parses to build a tree of objects that can be passed to the VIEW function for display. You don't have to use VID, but it allows you to write layouts in a much faster and user friendly way, than building the face object tree manually.

    If you look at it on a deeper level, dialects are forms of expression that allows you to "say" something that otherwise would require 10 times as much typing to "say". There are not many limitations in this, as you can even build dialects on top of other dialects. I can create a dialect that outputs VID code to pass to LAYOUT. You can of course say the same about traditional function abstraction in other programming languages, but you may not have the ability to form what you want to "say" as easily.

    In a program that I made here, I made a search dialect, that the user has direct access to in a text field in the user interface.

    All that is possible, when you learn how to juggle around with blocks, words and other datatypes.

    After that, there are "bindings". Those are a more advanced topic.

    Carl designed REBOL so that you can be very terse, when trying to "say" something, and I also recommend reading some of his code, as one can learn a lot from it.

    I hope some of this makes sense. :)

Share This Page