Experience with Anyscreen (Thin@)?

First time I raise my voice in any Clarion forums (except from a user-group in Norway back in 2005ish:)

Strange since I have been using Clarion since 1.x back in the late 1980’s and have had great benefit from it. I have spent hundreds (if not in the thousand) hours with the tool, and the move away from 6.x to 10 was for me the best move. Over the years I have been able to develop a lot of applications with great success. Part of the success has been to stay away from any side-steps (i.e. Clarion.net) and stay with the core product. And for me it has been remarkable stable (opposite to may of the contributors on the hub). It has also been great to work with the CapeSoft products.

Would I recommend the tool to my son? (Re. the discussion on providing a community edition free of charge). No. This technology is not what you would nominate for the University for the last 10+ years. Therefore I don’t think that is the way forward. My recommendation is to target business like my own (willing to pay for license cost) that has internal IT that needs tools to fix something with a low entry level. Then it is “Value for money”. As a general development tool I think you will be lost although I am still amazed that there seams to be no limit to what you are able to achieve in very short time.

So to my question: As I have managed to stay away from all the sidesteps, I am in a danger to do one now as I am considering Anyscreen.

Is it value for money?

1 Like

In addition to AnyScreen, you might want to consider TSPlus. Same end results for me, though I can imagine AnyScreen MIGHT have some capabilities unique to its Clarion association. However, TSPlus cost was lower and I have not encountered the bugs that were still present at the time my AnyScreen subscription expired after 1 year.

1 Like

Thanks for your input.
To my previous comment: “As a general development tool I think you will be lost..” I would correct and say that this is not related to all of you who have made Clarion dev. as your way of living, because I think knowing a tool to the fingertip makes you more effective and it will be a great benefit to stick with it (as I have been doing:). For new student, they would probably more look at Python, Swift or frameworks as Angular/React.

Hi Svein,
I’ve been using Clarion since 1988 and doing about the same as you it seems. I didn’t side-step into Clarion.net either. And I use many of the CapeSoft products as well. I like your assessment for using Clarion in an internal IT.
I bought the AnyScreen Server, but it has some issues that need to be improved. I don’t like how PDF report files are handled or how to access other programs, such as viewing Excel workbooks or online help, because they are separate programs.
I have all my Clarion users on a TSplus cloud servers now. I would recommend TSplus to you, either on a local server or on a cloud server. It solves all of the issues associated with deploying a LAN-based system, and has several other advantages.
I think AnyScreen will probably be successful, but for me, I will continue with TSplus. I can use Clarion, with CapeSoft tools, and view Excel workbooks and other things.
The TSplus speed is great, and accessing previous PDF reports is a great feature. And my users can access the server via a web browser (via an HTML5 client) from their PC, Mac, iPad or smartphone.
The only issue I have, with a few users who live in very rural areas, is their very slow Internet speed. But AnyScreen wouldn’t help any for them either. So there are no improvements or advantages with AnyScreen.
Bob Campbell

2 Likes

Thanks for valuable input.
I most probably will test out the TSplus product. Are you running Azure SQL Managed Instance or do you deploy it on a VM and use a standard MS SQL server (PaaS)?
Svein Bjørnstad

Maybe it would not take a whole lot of effort to test Anyscreen out with your app. You don’t need a license to test it.

Hi Svein,
I just have a Windows Server. No Azure. No VM: I have the whole server. I’m using MS SQL Server on one application and tps files for another application.
So, yes, pretty simple.
But quite reliable and fast. I have 16 GB of RAM which can handle about 40 concurrent users. Windows likes to limit memory usage to 80%.
I submitted a TSplus presentation proposal for the 2023 CIDC.
I’m no expert, but I have quite a few tips and suggestions.
You’re welcomed to ask me any questions.

Hi Bob,
How big is your application? 40 users in 16 GB of RAM is quite a bit higher than we can get.

Rick

Hi Rick,
My application’s exe is 16,578,560 bytes. We’ve had 44 users with the memory usage at 83%. Windows started to complain, “Low memory warning” or something like that.
I have a program that automatically logs off users who are idle for 20 minutes when the memory usage goes over 80%.
Bob

I’ve found user’s complain about the experience when the memory usage gets over 40%.

No. There doesn’t seem to be any slowing down of the program or anything I can detect when we have 5 users versus 35 users running the program.
Our server’s Internet speed is about 950 Mbps up and down, so we have plenty of bandwidth. And the CPU utilization rarely gets above 20%.
-Bob

What ever happened @Thin and was the react technology licensed to SV or SOLD by @Thin.. one assume there react library is replicable by AI anyway and recently AI has generated AZURE linux Service as an APP containers for our Binding as a service technology.. we are just looking at find some example of the THIN rendering of clarion windows.. not that we will be doing a Clarion in a browser as perhaps one day SV will emerge from hibernation and Anyscreen will actually be see some advancements. We are just interesting in looking at the controls , we suspect AI can generate a react GUI anyway for our AZURE server technology binding service technologies. The only technology we are missing is Clarion Queues but we think we have a solution coming for that on linux anyway which will also run on Windows.

If this runs on linux there is a case that a port to windows and mimicking the thin react client may also work.. but it would not be as simple as any screen as control equates ? would have to be mapped to meta Gui Names… how ever we have modelled MDI dynamic control creation using multi threading using notification… we may be able to run UBS binding services in an Azure Windows session … not as good as our linux stuff but it could work…

wgui(wevents(‘‘MULTIWINDOW’’,‘‘CLIENTS’’,‘‘ADDRESSES’’,‘‘FRANCE’’,‘‘DEMOGRAPHIC’’)); some script defaults to open if this is a window ;end;

Dynamic Control Creation in windows…

case event()

    of event:timer       
        
    of EVENT:Notify
        
         IF NOTIFICATION (NCode,NThread, NParam) !
            NAddress = NParam
            case NCode  
                
              !  of  GUIWEvents:IClaExecutePropValueaddress     
              !  IClaExecutePropValueref   
              !   IF NParam > 0
              !      ClaGuiPropControlref &= (NParam)
              !   End
              ! set populator interface    
                 
            ! Note: ControlEventFrameref instances are one message only and are disposed on each event.
            ! a new GUI Frame changes on the lObjectFrameIDNo ID.    
           of GUIWEvents:CLAControlEventsFrameStart    ! Event Frame with Event Start
                IF NAddress > 0
                    ControlEventFrameref  &=  (NAddress)
                    if not ControlEventFrameref &= null 
                       lObjectFrameIDNo = ControlEventFrameref.IGetSetControlEventFrame.getObjectFrameIDNo()
                      ! dispose(ControlEventFrameref)
                    End      
                END     
                
            of GUIWEvents:CLAControlEventsFrameEnd      ! Event Frame with Event End     
                IF NAddress > 0
                     ControlEventFrameref  &=  (NAddress)
                     ! Process any end frame options.
                   ! if not ControlEventFrameref &= null 
                   !    dispose(ControlEventFrameref)
                   ! END
                END
                lObjectFrameIDNo = 0
                    
            of GUIWEvents:CLAControlEventsFrame         ! Event Frame  
                IF NAddress > 0
                   ControlEventFrameref  &= (NAddress)
                   if not ControlEventFrameref &= null 
                      ! Call any services defined in       
                      ! dispose(ControlEventFrameref)
                   End      
                END     
                
              of GUIWEvents:ClaControlpositionaddress     
                IF NAddress > 0
                    ControlEventFrameref  &= (NAddress)
                   if not ControlEventFrameref &= null 
                        if ControlEventFrameref.IGetSetControlEventFrame.GetObjectFrameIDNo() = lObjectFrameIDNo   ! Event Frame Id Operation
                           CtrlPosrefaddress# = ControlEventFrameref.IGetSetControlEventFrame.GetObjectAddress() 
                           if  CtrlPosrefaddress# > FALSE 
                               CtrlPosref  &= (CtrlPosrefaddress#)  ! add controls to position
                           END 
                           if not LocalControlPosref &= NULL and not CtrlPosref &= NUll    
                              LocalControlPosref.CtrlPosref  &= CtrlPosref
                              add(LocalControlPosref)  ! One supported at a time.
                           END     
                       END
                   END
                END   
                
            of GUIWEvents:ClaControlPropaddress                
                ! IF NParam > 0 AND NOT ControlEventFrameref &= NULL AND ControlEventFrameref.IGetSetControlEventFrame.getObjectFrameIDNo() = lObjectFrameIDNo
                !    clactrprop# =  ControlEventFrameref.IGetSetControlEventFrame.getObjectAddress()
                !    ClaGuiPropControlref &= (clactrprop#)
                ! END
            of GUIWEvents:ClaControlpropcacheaddress   
                   !  IF NAddress > 0
                   !     Control Prop cache , Thread No of ClaBaseControl 
                   !  End     
            of GUIWEvents:CLAControlCreate    ! Nevents:, from thread, Param No
                    IF NAddress > 0
                       ControlEventFrameref &= (NAddress)
                       if not ControlEventFrameref &= NULL
                          clactrprop# =  ControlEventFrameref.IGetSetControlEventFrame.getObjectAddress()
                          ClaGuiPropControlref &= (clactrprop#)
                           if not ClaGuiPropControlref &= NULL  
                              GuiWinInstanceLock.Wait      
                              localCreateControl
                              if lcontrolID > false and not clip(ControlEventFrameref.ObjectName) = '' 
                                 GuiComponentsref.IGuiWindowControls.ControlCreated(ControlEventFrameref.ObjectName,lcontrolID) ! Update Control ID to Events
                              END  
                              LocalCreateQuickPositions(LWindowIdNo#)    
                              GuiWinInstanceLock.Release      
                          END
                          if not ControlEventFrameref &= NULL  
                             dispose(ControlEventFrameref)
                          END  
                        END
                    ! Note: Update state of frameevents may be set in the gui components.    
                    !   if not GuiComponentsref &= NULL 
                    !      GuiComponentsref.IGuiWindowControls.Windowsetevents(LWindowIdNo#,true ,lObjectFrameIDNo)  ! Events processed for this window  lObjectFrameIDNo still active
                    !   END
                 END       

UBS Script React Framework Integration Guide.pdf (355.1 KB)

What we are looking to do with this technology is to put older win32 Clarion hand coded apps in the cloud with projected React GUI generated. The Window Procedures would need to be code adjusted by Claude Code but if an application is mission critical and worth the code of a conversion the process would be almost painless.. This is not to say a Template generated APP could not be made to conform to this paradigm and move an APP away from the APP broker to standard tech stack on Azure.

REACT template are automatically generated by AI. Using industry standard syntax…

The UBS DATA statement creates structured, hierarchical data objects that can contain strings, numbers, arrays, and nested objects. This powerful feature makes it ideal for storing React component templates, properties, and styling information.

Basic DATA Syntax

componentData = data {
    name: "button",
    template: "<button onClick={onClick}>{children}</button>",
    props: ["onClick", "children", "disabled"]
};

Advanced DATA Structures

reactFramework = data {
    components: {
        button: {
            template: "<button className={className} onClick={onClick} disabled={disabled}>{children}</button>",
            defaultProps: { className: "btn", disabled: false },
            propTypes: ["onClick", "children", "disabled", "className"]
        },
        input: {
            template: "<input type={type} value={value} onChange={onChange} placeholder={placeholder} />",
            defaultProps: { type: "text", placeholder: "" },
            propTypes: ["type", "value", "onChange", "placeholder"]
        }
    },
    styles: {
        button: {
            primary: "bg-blue-500 text-white px-4 py-2 rounded",
            secondary: "bg-gray-300 text-gray-700 px-4 py-2 rounded",
            danger: "bg-red-500 text-white px-4 py-2 rounded"
        }
    }
};

Component Template Storage

Form Controls

formComponents = data {
    textInput: {
        template: "<div className='form-group'><label>{label}</label><input type='text' value={value} onChange={onChange} className='form-control' /></div>",
        props: ["label", "value", "onChange"],
        validation: {
            required: true,
            minLength: 0,
            maxLength: 255
        }
    },
    
    selectBox: {
        template: "<div className='form-group'><label>{label}</label><select value={value} onChange={onChange} className='form-control'>{options}</select></div>",
        props: ["label", "value", "onChange", "options"],
        optionTemplate: "<option value={value}>{text}</option>"
    },
    
    checkbox: {
        template: "<div className='form-check'><input type='checkbox' checked={checked} onChange={onChange} className='form-check-input' /><label className='form-check-label'>{label}</label></div>",
        props: ["checked", "onChange", "label"]
    },
    
    submitButton: {
        template: "<button type='submit' onClick={onSubmit} className={className} disabled={disabled}>{text}</button>",
        props: ["onSubmit", "text", "disabled", "className"],
        defaultProps: { text: "Submit", disabled: false, className: "btn btn-primary" }
    }
};

Layout Components

layoutComponents = data {
    window: {
        template: "<div className='window' style={{width: '{width}px', height: '{height}px', position: 'absolute', left: '{x}px', top: '{y}px'}}><div className='window-header'><h3>{title}</h3><button className='close-btn' onClick={onClose}>×</button></div><div className='window-content'>{children}</div></div>",
        props: ["width", "height", "x", "y", "title", "children", "onClose"],
        defaultProps: { width: 400, height: 300, x: 100, y: 100, title: "Window" }
    },
    
    panel: {
        template: "<div className='panel panel-{variant}'><div className='panel-header'>{header}</div><div className='panel-body'>{children}</div></div>",
        props: ["variant", "header", "children"],
        defaultProps: { variant: "default" }
    },
    
    grid: {
        template: "<div className='container-fluid'><div className='row'>{children}</div></div>",
        props: ["children"],
        columnTemplate: "<div className='col-{size}'>{content}</div>"
    },
    
    tab: {
        template: "<div className='tabs'><ul className='tab-list'>{tabHeaders}</ul><div className='tab-content'>{tabPanels}</div></div>",
        props: ["tabHeaders", "tabPanels"],
        headerTemplate: "<li className={active} onClick={onClick}>{title}</li>",
        panelTemplate: "<div className='tab-panel {active}'>{content}</div>"
    }
};

Data Display Components

dataComponents = data {
    list: {
        template: "<ul className='list-group'>{items}</ul>",
        props: ["items"],
        itemTemplate: "<li className='list-group-item' onClick={onClick}>{content}</li>"
    },
    
    table: {
        template: "<table className='table table-{variant}'><thead><tr>{headers}</tr></thead><tbody>{rows}</tbody></table>",
        props: ["headers", "rows", "variant"],
        headerTemplate: "<th scope='col'>{title}</th>",
        rowTemplate: "<tr>{cells}</tr>",
        cellTemplate: "<td>{content}</td>",
        defaultProps: { variant: "striped" }
    },
    
    card: {
        template: "<div className='card'><div className='card-header'>{header}</div><div className='card-body'><h5 className='card-title'>{title}</h5><p className='card-text'>{content}</p>{actions}</div></div>",
        props: ["header", "title", "content", "actions"]
    }
};

Event Handling Templates

eventHandlers = data {
    clickHandler: "const handleClick = (event) => { console.log('Button clicked:', event.target); };",
    
    changeHandler: "const handleChange = (event) => { setValue(event.target.value); };",
    
    submitHandler: "const handleSubmit = (event) => { event.preventDefault(); onSubmit(formData); };",
    
    closeHandler: "const handleClose = () => { setVisible(false); };",
    
    keyHandler: "const handleKeyPress = (event) => { if (event.key === 'Enter') { handleSubmit(event); } };",
    
    formValidation: "const validateForm = (data) => { const errors = {}; if (!data.name) errors.name = 'Name is required'; return errors; };"
};

CSS and Styling Data

styleDefinitions = data {
    themes: {
        light: {
            primary: "#007bff",
            secondary: "#6c757d",
            success: "#28a745",
            danger: "#dc3545",
            warning: "#ffc107",
            info: "#17a2b8",
            background: "#ffffff",
            text: "#212529"
        },
        dark: {
            primary: "#0d6efd",
            secondary: "#6c757d",
            success: "#198754",
            danger: "#dc3545",
            warning: "#ffc107",
            info: "#0dcaf0",
            background: "#212529",
            text: "#ffffff"
        }
    },
    
    layouts: {
        flexCenter: "display: flex; justify-content: center; align-items: center;",
        gridContainer: "display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px;",
        sticky: "position: sticky; top: 0; z-index: 1000;",
        fullHeight: "min-height: 100vh;",
        responsive: "@media (max-width: 768px) { flex-direction: column; }"
    },
    
    animations: {
        fadeIn: "@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }",
        slideIn: "@keyframes slideIn { from { transform: translateX(-100%); } to { transform: translateX(0); } }",
        bounce: "animation: bounce 0.3s ease-in-out;"
    }
};

Component Generation Functions

componentGenerator = data {
    createComponent: function(componentType, props) {
        template = reactFramework.components[componentType].template;
        // Replace template variables with actual props
        result = template;
        for (prop in props) {
            result = result.replace("{" + prop + "}", props[prop]);
        }
        return result;
    },
    
    createForm: function(fields, submitAction) {
        formHTML = "<form onSubmit={submitAction}>";
        for (field in fields) {
            fieldComponent = createComponent(field.type, field.props);
            formHTML = formHTML + fieldComponent;
        }
        formHTML = formHTML + createComponent("submitButton", {});
        formHTML = formHTML + "</form>";
        return formHTML;
    },
    
    createLayout: function(layoutType, content) {
        layoutTemplate = layoutComponents[layoutType].template;
        return layoutTemplate.replace("{children}", content);
    }
};

UBS Script Integration with WGUI

Basic WGUI React Generation

// Generate React components using WGUI events
wgui(wevents('WINDOW', 'MAIN', 'CREATE')) {
    // Create main window structure
    mainWindow = reactFramework.components.window;
    windowProps = data {
        title: "UBS Application",
        width: 800,
        height: 600,
        x: 100,
        y: 100
    };
    
    // Generate window HTML
    windowHTML = generateComponent("window", windowProps);
    
    // Add form inside window
    formFields = data [
        { type: "textInput", props: { label: "Name", value: "", onChange: "handleNameChange" } },
        { type: "textInput", props: { label: "Email", value: "", onChange: "handleEmailChange" } },
        { type: "submitButton", props: { text: "Save", onSubmit: "handleSubmit" } }
    ];
    
    formHTML = componentGenerator.createForm(formFields, "handleFormSubmit");
    finalHTML = windowHTML.replace("{children}", formHTML);
    
    // Output React component
    result = finalHTML;
};

Complex Multi-Window Example

wgui(wevents('MULTIWINDOW', 'CLIENT', 'ADDRESSES', 'FRANCE', 'DEMOGRAPHIC')) {
    // Create tabbed interface for client data
    tabData = data {
        tabs: [
            { id: "addresses", title: "Addresses", active: true },
            { id: "france", title: "France Data", active: false },
            { id: "demographic", title: "Demographics", active: false }
        ]
    };
    
    // Generate tab headers
    tabHeaders = "";
    for (tab in tabData.tabs) {
        activeClass = tab.active ? "active" : "";
        tabHeaders = tabHeaders + layoutComponents.tab.headerTemplate
            .replace("{active}", activeClass)
            .replace("{onClick}", "handleTabClick('" + tab.id + "')")
            .replace("{title}", tab.title);
    }
    
    // Generate tab panels
    addressPanel = createAddressForm();
    francePanel = createFranceDataGrid();
    demographicPanel = createDemographicCharts();
    
    tabPanels = layoutComponents.tab.panelTemplate.replace("{active}", "active").replace("{content}", addressPanel) +
               layoutComponents.tab.panelTemplate.replace("{active}", "").replace("{content}", francePanel) +
               layoutComponents.tab.panelTemplate.replace("{active}", "").replace("{content}", demographicPanel);
    
    // Combine into final tab component
    tabComponent = layoutComponents.tab.template
        .replace("{tabHeaders}", tabHeaders)
        .replace("{tabPanels}", tabPanels);
    
    // Wrap in main window
    windowComponent = layoutComponents.window.template
        .replace("{title}", "Client Management")
        .replace("{children}", tabComponent)
        .replace("{width}", "1200")
        .replace("{height}", "800");
    
    result = windowComponent;
};

Advanced React Hooks Integration

reactHooks = data {
    useState: {
        template: "const [{stateName}, set{StateNameCap}] = React.useState({initialValue});",
        imports: ["React"]
    },
    
    useEffect: {
        template: "React.useEffect(() => { {effectCode} }, [{dependencies}]);",
        imports: ["React"]
    },
    
    useContext: {
        template: "const {contextValue} = React.useContext({contextName});",
        imports: ["React"]
    },
    
    customHooks: {
        useForm: "const useForm = (initialValues) => { const [values, setValues] = React.useState(initialValues); const handleChange = (e) => setValues({...values, [e.target.name]: e.target.value}); return [values, handleChange]; };"
    }
};

State Management Templates

stateManagement = data {
    contextProvider: {
        template: "const {ContextName}Context = React.createContext(); const {ContextName}Provider = ({ children }) => { const [state, setState] = React.useState({initialState}); return <{ContextName}Context.Provider value={{ state, setState }}>{children}</{ContextName}Context.Provider>; };",
        props: ["ContextName", "initialState"]
    },
    
    reducer: {
        template: "const {reducerName}Reducer = (state, action) => { switch(action.type) { {cases} default: return state; } };",
        caseTemplate: "case '{actionType}': return { ...state, {stateChange} };"
    }
};

Complete Application Framework

applicationFramework = data {
    app: {
        template: "import React from 'react'; {imports} function App() { {hooks} return ( <div className='app'> {components} </div> ); } export default App;",
        props: ["imports", "hooks", "components"]
    },
    
    routing: {
        template: "import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; <Router> <Routes> {routes} </Routes> </Router>",
        routeTemplate: "<Route path='{path}' element={<{component} />} />"
    },
    
    apiService: {
        template: "const api = { get: async (url) => { const response = await fetch(url); return response.json(); }, post: async (url, data) => { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return response.json(); } };"
    }
};