B4J Tutorial [ABMaterial] Making Responsive Apps - please contribute your expertise!

Responsive ABMaterial Design - Making this a Reality!

ABM is great for easily making world class web apps - particularly for the desktop (big screens).
That being said, we often find ourselves less than impressed when we view our great app on a phone or tablet...
Personally, I have spent countless hours learning, discovering and coming to terms with this. I will share my experiences here. I hope you will too...

This tutorial will address the issue of creating / planning your app (pages) with responsiveness in mind.
There are many ways to skin this cat but first, we need to understand how ABM already supports the effort - which may not be fully understood.
@alwaysbusy has created tools to help with this, but here we shall learn by our code...

With smaller screens mostly in use these days, we need to take advantage of the platform and techniques to achieve a proper display on all media types - with the least amount of effort.

You are Invited to contribute meaningful content. Please post your tips / tricks / techniques and findings here.

Please DON'T ASK QUESTIONS here - Start a new thread (post)! Please don't muddy the waters in this thread...

Sections:

Row / Cell Structure - and other cool things...

Page Content Configuration Settings (Hide - Show)

Data Table Replacement using Custom Cards

Configuration Settings Using SideBar

Determine Device Size

UI Design Tricks

A Trap - Error in these Example...

Modal Sheet Footers


more to follow....
 
Last edited:

Harris

Expert
Licensed User
Longtime User
Row / Cell Structure - and other cool things...

This example:
AddRowsM2( 1, Not(is_phone), 0, 0, pad, pad, "rowtheme2") .AddCellsOSMP( 4, 0, 0, 0, 12, 6, 3, 0, 0, 0, 0, "cellcntr")

Row Structure:

AddRowsM2( 1, Not(is_phone), 0, 0, pad, pad, "rowtheme2")
( 1, Not(is_phone), 0, 0, pad, pad, "rowtheme2" )
No of Rows, centre in page, margins top , bottom, left, right, row theme

AddRowsM2 method creates (x) rows, centre or not, set all margins (top, bottom, left and right and apply a theme (if desired)...

The vars:
is_phone: Dim is_phone As Boolean = (page.IsPhone) Or (page.IsTablet) - cool thing...
If device IS a phone or a tablet, then act accordingly - which is NOT centered in page - extend to both edges...
NOTE: page.IsPhone or page.IsTablet can only be called from ConnectPage!!!! (we shall see this later)...

pad: Dim pad As Int = 5 ' define how far left and right we want to keep our row off the sides of the device screen - when not centered...

rowtheme2: this theme controls how the row will be formed... (a screenshot will show this later)
MyTheme.AddRowTheme("rowtheme2")
MyTheme.Row("rowtheme2").BackColor = ABM.COLOR_BLUEGREY
MyTheme.Row("rowtheme2").BackColorIntensity = ABM.INTENSITY_LIGHTEN3
MyTheme.Row("rowtheme2").BorderColor = ABM.COLOR_BLACK
MyTheme.Row("rowtheme2").BorderColorIntensity = ABM.INTENSITY_DARKEN3
MyTheme.Row("rowtheme2").BorderWidth = 3


Cell Structure:
AddCellsOSMP( 4, 0, 0, 0, 12, 6, 3, 0, 0, 0, 0, "cellcntr")


( 4, 0, 0, 0, 12, 6, 3, hd, hd, 0, 0, "cellcntr" )
No of Cells, offset small, medium, large, cell size small, medium, large , margins top, bottom, left , right, theme )

Vars:
hd: Dim hd As Int = 15 ' this keeps a padding for top and bottom...

AddCellsOSMP method creates (x) cells with device size offsets (not clearly understood), cell size dependant on the device, all margins and the theme to use....

!!! IMPORTANT !!!
Cell Size:
The magic part of automatic responsive apps!
In this example, I set a small cell size (phones) to 12 (full width), medium devices (tablets) to 6 (two cells) and desktops to 3 (which means 4 cells - 12 / 3 = 4)...

cellcntr: This theme controls the cell content - center it within the cell...
theme.AddCellTheme("cellcntr")
theme.Cell("cellcntr").Align = ABM.CELL_ALIGN_CENTER

The next posts will show how these settings affect the view on various devices - phones, tablets and desktops... and more...
 

Harris

Expert
Licensed User
Longtime User
Page Content Configuration Settings (Hide - Show)

Now let's examine the results of our code settings with pictures on each device.

AddRowsM2( 1, Not(is_phone), 0, 0, pad, pad, "rowtheme2")
Row settings: Phone & Tablet:
We defined 1 row, not centered in the page (uses full screen width) with a theme that places a black border around the row.
We also pad the row to keep the black border off the edge of the screen - more visually pleasing...

Desktop:
Since this form factor is not a phone or tablet, Center in Page is used... (do not use full screen width).

AddCellsOSMP( 4, 0, 0, 0, 12, 6, 3, 0, 0, 0, 0, "cellcntr")
Cell Settings:

We define 4 cells to handle all device types along with a theme that centers all controls within each cell.
Some controls, like dropdown combos will consume the entire cell width - by design.

Phone:
The value used in the "size Small" is 12. This creates 1 cell (12 / 12 = 1) when the form factor is a phone.
Note how the controls are stacked within the one cell.

Tablet:
The value used in the "size Medium" is 6. This creates 2 cells (12 / 6 = 2) when the form factor is a tablet.
The overall row height is shorter since the screen is wider and 2 cells contain the controls.

Desktop:
The value used in the "size Large" is 3. This creates 4 cells (12 / 3 = 4) when the form factor is a desktop screen.
Now each control is place in a single cell.

Note: Cell layout happens automatically because the ABM system has determined the form factor...












Effective Use of Screen Real Estate

The above pictures show the configuration settings for a page within the app.
They are initially used to extract the data from the database for display. On smallish devices they may needlessly consume much valuable screen space.

Show / Hide Page Configuration

Note the Amber floating button with directional arrows....
This is created using a method of the Navigation Bar - ExtraContent.
The button is called the ExtraHalfButton. It resides on the bottom edge of the Navigation Bar (sometimes called Title Bar if you prefer).

One could also use an ABMSideBar to accomplish the result of containing config settings, BUT, this requires a Top Item to access it - show the user it is available.
The ExtraHalfButton is small, visible and doesn't require a full Navigator Bar menu item. Looking at the phone picture above - there is no room for another top item in my example anyways!

Creating The page.NavigationBar.ExtraContent

The following is based on my findings in getting this to work successfully.

BuildPage(). This method is called prior to creating a web socket.
The point to be taken here is - create your standard NavigationBar. We will replace it in the ConnectPage().

B4X:
public Sub BuildPage()
    ' initialize the theme
    BuildTheme
    
    ' initialize this page using our theme
    page.InitializeWithTheme(Name, "/ws/" & ABMShared.AppName & "/" & Name, False, ABMShared.SessionMaxInactiveIntervalSeconds, theme)
    
    
    page.ShowLoader=True
    page.PageTitle = "Municipal Management"
    page.PageDescription = "Municipal Management System - ABM & B4J"
    page.PageHTMLName = "index.html"
    page.PageKeywords = ""
    page.PageSiteMapPriority = ""
    page.PageSiteMapFrequency = ABM.SITEMAP_FREQ_YEARLY
    page.DisableBackButton = False
    page.ShowConnectedIndicator = True

    ' Build the standard NavigationBar FIRST!       
    ABMShared.BuildNavigationBar(page, "Review Agenda", "../images/logo.png",  "Agenda", "", "")
    
    page.AddModalSheetTemplate(BuildMsgBoxYesNo)
    page.AddModalSheetTemplate(BuildComment)

    
End Sub

ConnectPage()

Read the comments in this method....

B4X:
public Sub ConnectPage()
    
    ' determine what device the app is running on... 
    ' Note: This can only be called from ConnectPage since we now have a web socket...
    Dim ip As Boolean = (page.IsPhone) Or (page.IsTablet)  ' (ip = is_phone)
    page.AddModalSheetTemplate(BuildInputSheet)

    
    Dim cc1,cc2,cc3,cc4 As Int = 0
    page.AddRowsM(1,True,0,0, "").AddCellsOS(4,0,0,0,3,3,3,"")
    
    cc1 = 1   ' these are cell positions - not actually needed but used during testing
    cc2 = 2
    cc3 = 3
    cc4 = 4

    Log("initing extra content")
    ' Now create a navbar with extra content (code to follow).  This clears the previous one and makes a new...
    ' Note: the last param tellls the method what device is being used...
    ABMShared.BuildNavigationBarEX(page, "Review Agenda", "../images/logo.png",  "Agenda", "", "", ip)
    
    ' causes the extra content to show - or hide (see method below)
    ExtraButton_Clicked("")
    
    ' create the page grid.  Our database data will show here...
    page.AddRows(1, True, "").AddCellsOSMP(1,0,0,0,12, 12, 12,0,0,0,0,"") '.AddCellsOSMP(1,0,0,0,2,2,2,0,0,0,0,"").AddCellsOSMP(1,0,0,0,2,2,2,0,0,0,0,"")
    page.AddRows(4,True, "").AddCells12(1,"")
    page.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components

    ' dummy spacer for top of page
    Dim searchUser As ABMLabel
    searchUser.Initialize(page,  "lb1",   " ", ABM.SIZE_H5,  False, "lightblue")
    page.CellR(0,1).AddComponent(searchUser)
    
    ' determine the permission level from the active logged in user...
    ActiveUserId = page.ws.Session.GetAttribute2("UserID",0)
    
    ' set the public device size
    ABMShared.setScrnSize( page)
    
    Dim mtlist As ABMCombo
    mtlist.Initialize(page,"mtlist1","SELECT AGENDA DATE",350,"")
    
    Dim SQL As SQL = DBM.GetSQL
    Dim ctype As List = DBM.SQLSelect(SQL, "SELECT  createdate, name, id, agndoc From  cmeetmast where comp_id = " &Main.comp_id& " ORDER BY createdate DESC LIMIT 13 " , Null)
    
    For i = 0 To ctype.Size-1
        Dim ctt As Map = ctype.Get(i)
        Dim cnum As Int = ctt.Get("id")
        Dim cname As String = ctt.Get("name")
        Dim agn As String = ctt.Get("agndoc")
        Log("What is agn doc name: "&agn)
        If agn.Length > 8 Then
           mtlist.AddItem( ""&cnum, cname, BuildSimpleItem("S1"&cnum, "mdi-action-grade", "{NBSP}{NBSP}"&cname&""))
        End If           
    Next
    
    ' !!! NOTE - IMPORTANT !!!
    ' the extra content is a container...  add the component to the extracontent container cell
    page.NavigationBar.ExtraContent.CellR(1,cc1).AddComponent(mtlist)


    Dim agend As ABMCombo
    agend.Initialize(page,"agend","SELECT AGENDA ITEM",300, "")

    Dim sql_str As String = "Select * from cagenda where comp_id = 0 OR comp_id = "&Main.comp_id
    Dim head As List = DBM.SQLSelect(SQL, sql_str ,Null)

    If head.Size > 0 Then
      For i = 0 To head.Size -1
        Dim mp As Map = head.Get(i)
        Dim id As Int = mp.Get("id")
        Dim fn As String = mp.Get("name")
        agend.AddItem(""&id,  fn, BuildSimpleItem("11", "mdi-action-star-rate", fn))
      Next   
    End If
    
    DBM.CloseSQL(SQL)
    page.NavigationBar.ExtraContent.CellR(0,cc2).AddComponent(agend)


    Dim btnshowallagenda As ABMSwitch
    btnshowallagenda.Initialize2( page , "btnshowallagenda", "{C:#000000}{B}SHOW ALL ITEMS{/B}{/C}" , "", " ",  False, "swt1")
    btnshowallagenda.State = True
    page.NavigationBar.ExtraContent.CellR(0,cc3).AddComponent(btnshowallagenda)
    

    Dim btnshowallcomm As ABMSwitch
    btnshowallcomm.Initialize2( page , "btnshowallcomm",  "{C:#000000}{B}SHOW ALL COMMENTS{/B}{/C}", ""," ", False, "swt1")
    btnshowallcomm.State = True
    page.NavigationBar.ExtraContent.CellR(0,cc4).AddComponent(btnshowallcomm)


    ' these are local page items... added to the page...
    Dim newcases As ABMLabel
    newcases.Initialize(page,  "newcases",   "{NBSP}{NBSP} Motions Presented ", ABM.SIZE_H5,  False, "lightblue")
    page.Cell(4,1).AddComponent(newcases)
    
    
    Dim pagination As ABMPagination
    pagination.Initialize(page, "pagination", 2, True, True, "")
    pagination.SetTotalNumberOfPages(0)
    page.Cell(4,1).AddComponent(pagination)
    
    
    
    ' This WAS the old method of using a responsive table - Looks and Works horribly!
    ' This system is replace on small devices using custom cards.
    ' It is still used when using app on a desktop - where it looks and works fantastico...
    Dim tblCases As ABMTable
    tblCases.IsResponsive = True
    tblCases.Initialize(page, "tblCases", False, False, True, "tbltheme")

    If ABMShared.ScrnSize = 0 Then
        tblCases.SetHeaders(         Array As String ("ID"  ,"Num" , "Meeting Date"  , "Summary"    , "Description"    , "Attachments"    , "Comment","View", "Delete"))
        tblCases.SetHeaderThemes(    Array As String ("bg"  ,"bg"  ,"bg"    , "bg"      , "bg"       ,"bg"           , "bgc"      , "bgc" , "bgc"   ))
        tblCases.SetHeaderHeights(   Array As Int    (0     , 0    ,0    , 0         , 0          , 0             , 48         , 48  , 48    ))   
        tblCases.SetColumnVisible(   Array As Boolean(False, True    ,True  , True      , True       , True          , True  ,True     , False    ))
    '    tblCases.SetColumnWidths(  Array As Int(      1,      0  ,         0 ,              0,              0,             0,            0,    0,      0 ))   
    Else   
        tblCases.SetHeaders(         Array As String ("ID"  ,"Num" , "Meeting Date"  , "Summary"    , "Description"    , "Attachments"    , "Comment","View", "Delete"))
        tblCases.SetHeaderThemes(    Array As String ("bg"  ,"bg"  ,"bg"    , "bg"      , "bg"       ,"bg"           , "bgc"      , "bgc" , "bgc"   ))
    '    tblCases.SetHeaderHeights(   Array As Int    (0     , 0    ,0    , 0         , 100          , 70             , 48         , 48  , 48    ))   
        tblCases.SetColumnVisible(   Array As Boolean(False, True    ,True  , True      , True       , True          , True  ,True     , False    ))
        tblCases.SetColumnWidths(  Array As Int(      1,      0  ,         0 ,              0,             600,             0,            0,    0,      0 ))   
    End If
    
    tblCases.SetFooter("Totals: 0",  12, "bg")
    page.Cell(5,1).AddComponent(tblCases)   
    
    If ABMShared.ScrnSize < 2 Then
        ' hide this table on all small devices...
        tblCases.Visibility = ABM.VISIBILITY_HIDE_ALL
    End If
    
    ' configure our menu sidebar with options appropriate for this logged in user...
    ABMShared.AdminPages(page)
    
    page.Refresh ' IMPORTANT

    ' NEW, because we use ShowLoaderType=ABM.LOADER_TYPE_MANUAL
    page.FinishedLoading 'IMPORTANT

End Sub

The ExtraButton_ Clicked Event

B4X:
Sub ExtraButton_Clicked(Target As String)
    ' declare in Class Globals...
    'Private exc As Boolean = False

' since there is NO extracontent_opened property - we must determine (control) the state with our own boolean var
    
    If exc Then
        page.NavigationBar.CloseExtraContent
    Else
        page.NavigationBar.OpenExtraContent
    End If
    exc = Not(exc)
        
End Sub

The ABMShared - BuildNavigationBarEX()
Note: This extracontent was built specifically for this example.
It could be designed to be much more versitile by passing a map of all row, cell params.... It's all up to you...

B4X:
Sub BuildNavigationBarEX(page As ABMPage, Title As String, logo As String, ActiveTopReturnName As String, ActiveSideReturnName As String, ActiveSideSubReturnName As String, ip As Boolean)
    
' this is the same as a normal navbar...

    Dim sbtopimg As ABMImage
    sbtopimg.Initialize(page,  "sbtopimg",  logo, 1)
    sbtopimg.SetFixedSize( 236, 49)

    page.NavigationBar.Initialize(page, "nav21",  ABM.SIDEBAR_MANUAL_ALWAYSHIDE, Title,  True,  True, 300, 48, sbtopimg, ABM.COLLAPSE_ACCORDION, "nav1theme")
    
    ' CLEAR any existing items....
    page.NavigationBar.Clear
    
    
    page.PaddingBottom = 100
    
    page.NavigationBar.ActiveTopReturnName = ActiveTopReturnName
    page.NavigationBar.ActiveSideReturnName = ActiveSideReturnName
    page.NavigationBar.ActiveSideSubReturnName = ActiveSideSubReturnName

    page.NavigationBar.AddTopItem("Help",  "Help", "mdi-action-help",  "../AboutPage",  False)
    page.NavigationBar.AddTopItem("LogOff",  "Log Off", "mdi-action-exit-to-app",  "",  False)
    page.NavigationBar.AddSideBarItem( "About",   "About / Help", "mdi-communication-live-help",  "../AboutPage")
    
    page.NavigationBar.AddSideBarDivider '("")
    
    
    page.NavigationBar.AddSideBarItem("Cases", "Manage Issues", "mdi-action-dashboard", "../OverviewCasesPage")
    page.NavigationBar.AddSideBarItem("Meetings", "Manage Meeting Tasks", "mdi-action-store", "../meetingsPage")
    page.NavigationBar.AddSideBarItem("Future", "Manage Future Motions", "mdi-action-store", "../FuturePage")
    page.NavigationBar.AddSideBarItem("Contracts", "Manage Contractors", "mdi-action-assignment", "../ContractPage")
    
    page.NavigationBar.AddSideBarItem("Agenda", "Review Agenda", "mdi-action-group-work", "../agendaPage")
    page.NavigationBar.AddSideBarItem("Feedback", "App FeedBack", "mdi-action-question-answer", "../feedbackPage")

    page.NavigationBar.AddSideBarItem(  "Equip",   "Equipment",  "mdi-action-settings-applications",  "../AboutPage")
    page.NavigationBar.AddSideBarSubItem("Equip",  "Map", "Show Location"   , "mdi-maps-place","../locationPage")
    page.NavigationBar.AddSideBarSubItem("Equip",  "Usage", "Utilization"   ,"mdi-notification-drive-eta","../drvshiftsPage")
    page.NavigationBar.AddSideBarSubItem("Equip",  "Insp", "DVIR Inspections", "mdi-content-backspace", "../DVIRPage")
    
    page.NavigationBar.AddSideBarDivider '("")
    
    Log(" added main menu items")


' Here we build the extra content container.

    Dim pad As Int = 5
    Dim hd As Int = 15

    page.NavigationBar.InitializeExtraContent("extracontent", False, "")
    ' dummy row - may be used later...
    page.NavigationBar.ExtraContent.AddRowsM(1,True,0,0 ,"").AddCellsOSMP(1,0,0,0,12,12,12,0,0,20,20,"")
    
    ' the actual row to contain our components...
    page.NavigationBar.ExtraContent.AddRowsM2(   1,  Not(ip),  0, 0,pad,pad, "rowtheme2").AddCellsOSMP( 1, 0,0,0,  12,6,3, 0,0,0,0,"cellcntr").AddCellsOSMP(1,0,0,0, 12,6,3,0,0,0,0,"cellcntr").AddCellsOSMP(1,0,0,0, 12,6,3, hd, hd,0,0,"cellcntr").AddCellsOSMP(1,  0,0,0, 12,6,3, hd,  hd,0,0,"cellcntr")
    
    ' another dummy row...
    page.NavigationBar.ExtraContent.AddRowsM(1,True,0,0,"").AddCellsOSMP(  1,0,0,0, 12,12,12, 0,40,20,20,"")
    
    
    ' BUILD IT!
    page.NavigationBar.ExtraContent.BuildGrid
    
    ' add the half button...
    page.NavigationBar.InitializeExtraHalfButton("ExtraButton", "fa fa-arrows-v", ABM.BUTTONSIZE_NORMAL, ABM.HALFBUTTON_RIGHT, "btnamber")

End Sub


Even thou we have much vertical space on a destop, we imploy the same design... After all, we are trying to keep things simple....

Next example.... Replacing the ABM responsive grid using Custom Cards.... (on small devices)...
 

Harris

Expert
Licensed User
Longtime User
Data Table Replacement using Custom Cards

The biggest reason I was so impressed with ABM, from the get go, was the data table component. - ABMTable.
After fighting with all else on the market at that time, I knew my prayers had been answered by learning and experiencing this component.
Most web apps deal with manipulating and presenting data - in an intuitive table format that can handle all the required tasks (present, edit, save, refresh, navigate, etc).

Desktop Table
Shown here is a typical / simple ABMTable. It includes a title bar for column names, rows to present fields of each record and floating buttons to manipulate the data.
Perfect for large media, this construct gets a little unwieldy on smaller devices...



Custom Cards vrs ABMTable - Responsive Mode

Don't get me wrong, you can use a Responsive ABMTable on small devices - it just looks and feels wrong to me (and apparently others...).
The original creators of this Material Framework did what they could with their responsive attempt of this table.
As well, you are also free to use a non-responsive table on a small device - but beware - users must scroll the screen left and right to view portions of the entire table - not really a practical modern approach... But hey, with this, you didn't have to recode a dang thing!

ABMCustomCards can be a suitable replacement for a table component ( I am sure there are others ), but you must code the all UI and functionality for your needs.
This approach does have its' own set of complexity - all of which can be overcome with proper instruction and discovery...

Thanks to the power of B4J / ABM and your understanding of both, a modern / practical / visual pleasing solution can be achieved.



Coding The ABMCustomCard

This example uses a MySQL database and tables to fetch records for display on each card (actually irrelevant).
Read the comments within the code....

B4X:
private Sub LoadCases(fromPage As Int)   
    
    Dim actid As String = ActiveCaseId
    Log("Active case: "&ActiveCaseId)
    Dim tblCases As ABMTable = page.Component("tblCases")   
    
    DateTime.DateFormat = "yyyy-MM-dd"
    DateTime.TimeFormat = "HH:mm"
    Dim SQL As SQL = DBM.GetSQL
    Dim SQL_str, SQL_cnt As
    
    ABMShared.setScrnSize(page)
'    page.Pause

    ' we need to know how many records we have - controls pagination
    SQL_cnt = "SELECT cmeeting.CaseID from cmeeting  Where cmeeting.mastid = "&actid  &" OR cmeeting.mnum = '' AND cmeeting.comp_id = "&Main.comp_id
    ' get a certain number for records - according to which page of pagination we are on...
    SQL_str = "SELECT * From cmeeting  Where cmeeting.mastid = "&actid& " OR mnum = '' AND cmeeting.comp_id = "&Main.comp_id&" LIMIT " & ((fromPage - 1) * MaxRows) & "," & MaxRows
  
    Dim cntcases As List = DBM.SQLSelect(SQL, SQL_cnt, Null)
    Dim numcases As Int = cntcases.Size

    Log(" Sql count statement: "&SQL_cnt)
    Dim cases As List = DBM.SQLSelect(SQL, SQL_str, Null)
    
    If cases.Size = 0 And fromPage > 1 Then
        ' we are on a page without any lines (maybe removed by other user?)
        DBM.CloseSQL(SQL)
        fromPage = fromPage - 1
        LoadCases(fromPage)
        Return
    End If
    
    If ABMShared.ScrnSize < 2 Then
        ' if we are on a phone or table - hide the table component - we will use cards....
        tblCases.Visibility = ABM.VISIBILITY_HIDE_ALL
        page.Row(6).RemoveAllComponents
    Else
        tblCases.Visibility = ABM.VISIBILITY_ALL
        tblCases.Clear
    End If
        
    Log(" what is screen size in loadcases "& ABMShared.ScrnSize)

    For i = 0 To cases.Size - 1
        Dim tblFields As Map = cases.Get(i)
        
        ' determine if device is a phone or a tablet...
        ' can be replaced with - If page.IsPhone Or page.IsTablet Then
        If ABMShared.ScrnSize < 2 Then
            
            ' we use this to uniquely identify each components ID property
            Dim uid  As String = tblFields.Get("caseid") ' an AutoInc table primary key...


            ' create an action button. see the ab_clicked method (below) to see how we deal with which card action button was clicked..
            Dim act As ABMActionButton 
            act.Initialize2(page, "ab", "fa fa-tasks", "", ABM.ACTIONBUTTON_DIRECTION_LEFT,  ABM.ACTIONBUTTON_POSITION_TOPRIGHT,  0,0, "btngreen")
            act.Tag = tblFields
              
            ' now create the floating buttons for actions of the ABMActionButton
            Dim btnEdit As ABMButton
            btnEdit.InitializeFloating(page, "e"&uid, "mdi-action-visibility", "")
            btnEdit.Tag = tblFields
'               
            Dim btnRole As ABMButton
            btnRole.InitializeFloating(page, "r"&uid, "mdi-communication-comment",  "amber")
            btnRole.Tag = tblFields



            ' create a custom card for each record.... (ie. THIS record) - and theme it...
            Dim crd As ABMCustomCard
            crd.Initialize(page, "crd"&uid ,  "crd")
            
            
            Dim lab As ABMLabel
            Dim credate As String = ABMShared.GetMeetDate(tblFields.Get("mastid"))
            Dim nmum As String = tblFields.Get("mnum")
            
            lab.Initialize(page, "id"&uid, "{B}Date:{/B} "&credate&" {NBSP}{NBSP}   {B}#:{/B} {U}"&nmum&"{/U}" ,ABM.SIZE_H5,  False,"")
            lab.Truncate = True
            lab.IsFlowText = True
            
            
            ' you must be careful with NULL fields...  or you will crash!
            If tblFields.Get("casesummary") = Null Or tblFields.Get("casesummary") = "" Then
                Dim descs As String = "-" 'tblFields.Get("casesummary")
            Else
                Dim descs As String = tblFields.Get("casesummary")
            End If

            Dim lab1 As ABMLabel
            lab1.Initialize(page, "did"&uid, "{B}Title:{/B} "&descs ,ABM.SIZE_H5,  False,"")
            lab1.Truncate = True
            lab1.IsFlowText = True

            If tblFields.Get("casedescription") = Null Or tblFields.Get("casedescription") = "" Then
                Dim desc As String = ""
            Else
                Dim desc As String = tblFields.Get("casedescription")
                If desc.Length > 80 Then
                    ' trim the text so it doesn't spill off the screen...
                    desc = desc.SubString2(0 ,79)
                End If
                
                desc = desc&"{BR}"  ' BR is a line break...
                Dim agncomment As ABMLabel
                agncomment.Initialize(page,"agncmtx"&i,"",ABM.SIZE_PARAGRAPH,False,"")
                
                ' these are comments
                Dim cmtmap As Map
                cmtmap.Initialize
                ' go get comments from an associated table - if any...
                cmtmap = GetCommentMap(tblFields.Get("caseid"),cmtmap)
                
                ' we need a var to control the HEIGHT of the card....
                Dim xtra As Int = 0
                
                If cmtmap.Size > 0 Then
                    For k = 0 To cmtmap.Size -1
                        Dim cmtuser As String = GetOwner(cmtmap.GetKeyAt(k),False)
                        cmtuser = "{B} Comment: {/B} "&cmtuser.ToUpperCase&" - "&cmtmap.GetValueAt(k)
                        ' for each additional comment, increase the card height...
                        xtra = xtra + 30
                        If ABMShared.ScrnSize  = 0 Then
                            If cmtuser.Length > 80 Then
                                cmtuser = cmtuser.SubString2(0 ,79)
                            End If
                        End If
                        desc = desc&"{BR}"&cmtuser
                    Next
                    If cmtmap.Size = 1 Then
                        ' incase we only have one comment - set the extra height to 50
                         xtra = 50
                    End If
                End If
            End If

            Dim lab2 As ABMLabel
            lab2.Initialize(page, "gid"&uid, "{B}Desc:{/B} "&desc ,ABM.SIZE_H5,   False,"")
            lab2.Truncate = True
            lab2.IsFlowText = True


            ' since we dont really have room to show any attachment links - we shall simply indicate if there are any to view...
            Dim motion As String = GetAttaches( tblFields.Get("caseid"),"--")
            
            Dim mm As String
            If motion.Length > 3 Then
                mm = "fa fa-check-square"
            Else
                mm = "fa fa-window-close"
            End If

            Dim lab3 As ABMLabel
            If mm = "fa fa-check-square" Then
                lab3.Initialize(page, "mid"&uid, "{B}{NBSP}{C:#00CC00} Has Attachments{/B}{/C} " ,ABM.SIZE_H5,   False, "att1")
            Else
                lab3.Initialize(page, "mid"&uid, "{B}{NBSP}{C:#ff0000} No Attachments{/B}{/C} " ,ABM.SIZE_H5,   False, "att2")
            End If


            ' format the icon (green check when attaches, red X when none...
            lab3.IconName = mm
            lab3.IconAlign = ABM.ICONALIGN_LEFT
            lab3.VerticalAlign = True
            lab3.IsFlowText = True

            ' create a container which shall hold all our text components
            Dim cont As ABMContainer
            cont.Initialize(page,"cont"&uid, "cont")
            cont.AddRowsMV(  1 , False, 0, 0, ABM.VISIBILITY_ALL, "").AddCellsOSMP(1,  0,0,0,  12, 12, 12, 0 ,0,20, 0, "")
            cont.BuildGrid

            ' create a rounded border of the container
            cont.SetBorderEx( ABM.COLOR_BLACK,ABM.INTENSITY_DARKEN1,  2,ABM.BORDER_GROOVE, "20px")
            
            ' combine all text items
            lab.Text = lab.Text&"{BR}"&lab1.text&"{BR}"&lab2.Text
            ' add them to the container
            cont.Cell(1,1).AddComponent(lab)
            cont.Cell(1,1).AddComponent(lab3)
            
            ' add the ABMActionButton to the container
            cont.Cell(1,1).AddArrayComponent( act,  "edt")
            cont.Tag = tblFields

            ' set the height of the card - adjust accordingly....
            Dim fixed As Int = 130 + xtra
            If page.IsTablet Then
                fixed = 170 + xtra
            End If
            Log(" what is card height: "&fixed)   
            cont.Row(1).SetFixedHeight(fixed, False )
                
            ' use the tag to hold the map of all data for this record...  useful in events!   
            crd.Tag = tblFields
            act.AddMenuButton( btnEdit)
            act.AddMenuButton( btnRole)
            
            ' set the customcard's FrontTitle to the container component...
            
            crd.SetFrontTitleComponent(cont)
            
            ' remove this existing html to get rid of troubling reveal (3 vertical dots)!
            crd.HTMLAddClass( "card-reveal" )
            
            ' finish by adding the custom card to the page cell...
            page.Row(6).Cell(1).AddComponent(crd)


        Else
           '  a desktop table code....  not needed here... 
        End if
    Next
    If ABMShared.ScrnSize < 2 Then
        page.Refresh
    Else
        tblCases.SetFooter(" Total Motions: "&numcases,  12,"bg")
        tblCases.Refresh
    End If
    
    DBM.CloseSQL(SQL)
'    page.Resume
    Dim pagination As ABMPagination = page.Component("pagination")
    If (numcases Mod MaxRows > 0) Or (numcases = 0) Then
        numcases = numcases/MaxRows + 1
    Else
        numcases = numcases/MaxRows
    End If
    Log(" num cases pages: "&numcases)
    
    pagination.SetTotalNumberOfPages(numcases)
    pagination.SetActivePage(fromPage)
    pagination.Refresh
End Sub

The ab_Clicked() Method
This determines what to call based on the ActionButton selected...

B4X:
Sub ab_clicked(target As String, subtarget As String)

    Dim action As String = subtarget.SubString2(0,1)
    Dim uid As Int = subtarget.SubString( 1)
    
    ' get the ID from the card component
    Dim crd As ABMCustomCard = page.Component("crd"&uid)
    
    ' get a map of data from the card tag...
    Dim m As Map = crd.Tag

    Log(" what is action: "&action&"  uid: "&uid)
    Select action
        Case "e"    'view in a modal sheet
            TBLItemView(uid , m.Get("casesummary"))
        Case "r"   ' add a new comment in a modal sheet
            TBLItemComm(uid , m.Get("casesummary"))
    End Select
    
    
End Sub

Sure, there is much manipulation going on here - but thats why we are called developers... or as in my case - a HACK! HA!

Perhaps an ExtraSideBar will be the next example - we shall see.....
 
Last edited:

Harris

Expert
Licensed User
Longtime User
Configuration Settings Using SideBar

Using the Extra Sidebar provides another method of hiding your settings until needed...
Unlike the main navigatorbar sidebar, there is no method to swipe / slide the extra sidebar open on phones / tablets.
Also, one must typically add a top item to open / close the extra sidebar...

In this example, we shall use a little trick to overcome the shortcomings of the Extra Sidebar component.
We shall use the ExtraContent (as explained above) to provide us with the half button - and its' click method to show / hide the extra sidebar.

The code sections below explain how this accomplished...



ConnectPage()

Add this section to the bottom of the connectpage (use the one from extra content example above) to make an extra sidebar.

B4X:
    Dim extraSideBar As ABMSideBar

' Note the 4 th param ( 80 ) - This addes extra space to completely show the button when sidebar is open....
    extraSideBar.InitializeAsPanel( page, "extrasidebar", 450,  80,   Null,   "nav3theme",  "extrasidebarcontent",  "")
    extraSideBar.Content.AddRowsM(1, False, 0, 0, "").AddCells12MP(1,   0, 0,  0,  0, "")
    extraSideBar.Content.BuildGrid
   
    ' call the method which builds an extra sidebar
    extraSideBar.Content.Cell(1,1).AddComponent(  BuildSideBarComponent )   'page) )
   
    ' NOTE: This call is required to actually add a sidebar.
    ' Do note add any itemtext or icon as a top item...
    page.NavigationBar.AddTopItemWithSideBar(  "SideBar",   "",  "",  "",   False,  extraSideBar)
   
    page.Refresh ' IMPORTANT

    page.FinishedLoading 'IMPORTANT

    ' call the extra button click methos to open the sidebar
    ExtraButton_Clicked("")

The BuildSidebar Method
Use a container to add all config components to...

B4X:
Sub BuildSideBarComponent()  As ABMContainer
    Dim ItemCont As ABMContainer
    ItemCont.Initialize(page, "SideBarCont",  "")
    ItemCont.AddRowsM2(   1,  False,  10, 0, 5,5, "rowtheme2").AddCellsOSMP( 1, 0,0,0,  12,12,12, 0,0,0,0,"cellcntr").AddCellsOSMP(1,0,0,0, 12,12,12,0,0,0,0,"cellcntr").AddCellsOSMP(1,0,0,0, 12,12,12, 20, 20,0,0,"cellcntr").AddCellsOSMP(1,0,0,0, 12,12,12, 20, 20,0 ,0,"cellcntr")
    ItemCont.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components
 
    Dim cc1,cc2,cc3,cc4 As Int = 0
   
    cc1 = 1   ' these are cell positions - not actually needed but used during testing
    cc2 = 2
    cc3 = 3
    cc4 = 4

    Log("initing sidebar content")

    ' set the public device size
    ABMShared.setScrnSize( page)
   
    Dim mtlist As ABMCombo
    mtlist.Initialize(page,"mtlist1","SELECT AGENDA DATE",350,"")
   
    Dim SQL As SQL = DBM.GetSQL
    Dim ctype As List = DBM.SQLSelect(SQL, "SELECT  createdate, name, id, agndoc From  cmeetmast where comp_id = " &Main.comp_id& " ORDER BY createdate DESC LIMIT 13 " , Null)
   
    For i = 0 To ctype.Size-1
        Dim ctt As Map = ctype.Get(i)
        Dim cnum As Int = ctt.Get("id")
        Dim cname As String = ctt.Get("name")
        Dim agn As String = ctt.Get("agndoc")
        Log("What is agn doc name: "&agn)
        If agn.Length > 8 Then
            mtlist.AddItem( ""&cnum, cname, BuildSimpleItem("S1"&cnum, "mdi-action-grade", "{NBSP}{NBSP}"&cname&""))
        End If
    Next
   
    ' !!! NOTE - IMPORTANT !!!
    ' this is a container...  add the component to the container cell
    ItemCont.CellR(0,cc1).AddComponent(mtlist)


    Dim agend As ABMCombo
    agend.Initialize(page,"agend","SELECT AGENDA ITEM",300, "")

    Dim sql_str As String = "Select * from cagenda where comp_id = 0 OR comp_id = "&Main.comp_id
    Dim head As List = DBM.SQLSelect(SQL, sql_str ,Null)

    If head.Size > 0 Then
        For i = 0 To head.Size -1
            Dim mp As Map = head.Get(i)
            Dim id As Int = mp.Get("id")
            Dim fn As String = mp.Get("name")
            agend.AddItem(""&id,  fn, BuildSimpleItem("11", "mdi-action-star-rate", fn))
        Next
    End If
   
    DBM.CloseSQL(SQL)
    ItemCont.CellR(0,cc2).AddComponent(agend)


    Dim btnshowallagenda As ABMSwitch
    btnshowallagenda.Initialize2( page , "btnshowallagenda", "{C:#000000}{B}SHOW ALL ITEMS{/B}{/C}" , "", " ",  False, "swt1")
    btnshowallagenda.State = True
    ItemCont.CellR(0,cc3).AddComponent(btnshowallagenda)
   

    Dim btnshowallcomm As ABMSwitch
    btnshowallcomm.Initialize2( page , "btnshowallcomm",  "{C:#000000}{B}SHOW ALL COMMENTS{/B}{/C}", ""," ", False, "swt1")
    btnshowallcomm.State = True
    ItemCont.CellR(0,cc4).AddComponent(btnshowallcomm)
 
    Return ItemCont
 
End Sub

The ExtraButton Click Method
It simply toggles the visible state (opened / closed) of the sidebar


B4X:
Sub ExtraButton_Clicked(Target As String)
   
' comment out extracontent - we dont want to open this - ever...  
   
    ' declared in Class Globals...
    'Private exc As Boolean = False

' since there is NO extracontent_open property - we must determine (control) the state with our own boolean var
   
'    If exc Then
'        page.NavigationBar.CloseExtraContent
'        page.CloseSideBar("extrasidebar")
'    Else
'        page.NavigationBar.OpenExtraContent
'        page.ShowSideBar( "extrasidebar")

'    End If
'    exc = Not(exc)

' simple method to control sidebar visibility!
    page.ToggleSideBar("extrasidebar")


' NOTE: one could also use this process to open and close the sidebar - but toggle is much easier!

'Dim sb As ABMSideBar = page.GetSideBar("SideBar")
   
'    If sb.IsOpen Then
'        page.CloseSideBar("extrasidebar")
'    Else
'        page.ShowSideBar( "extrasidebar")
'    End If
       
End Sub
 

Harris

Expert
Licensed User
Longtime User
Determine Device Size

This example is just a snippet to determine what kind of device the app is running on (phone, tablet or bigger (desktop)).

In ABMShared:

B4X:
 Public Sub IsSmall(pg As ABMPage)
   
' process globals:
'    Public SmallDevice As Boolean = False


    If (pg.IsPhone) Or (pg.IsTablet) Then
        SmallDevice = True
    Else
        SmallDevice = False
    End If
   
End Sub
B4X:

NOTE: This sub (ABMShared.IsSmall) can only be called from ConnectPage()

B4X:
public Sub ConnectPage()
   
    ' determine what device the app is running on...
    ' Note: This can only be called from ConnectPage since we now have a web socket...
   
    ABMShared.IsSmall(page)   ' set a public var - SmallDevice
   
' normally, we build additional page sheets in BuildPage() - however this sub has no idea as to the device size... so we build them here...
    page.AddModalSheetTemplate( BuildInputSheet )
'..........

End Sub

The BuildInputSheet()
If the device is a phone or tablet, make the modal sheet full screen (use full width and height on these devices).
This is the only way I can deal with the form factor - currently.

B4X:
Sub BuildInputSheet() As ABMModalSheet
    Dim inp As ABMModalSheet
    inp.Initialize(page, "inpview",   True,   False,  "sheet1")
    inp.IsDismissible = False
   
    ' on smallish devices - make the modal sheet full screen!!!
    If ABMShared.SmallDevice Then  ' this var was set to determine what device the app is running on...
        inp.Size = ABM.MODALSHEET_SIZE_FULL
        Log(" built full screen")
    Else
        inp.Size = ABM.MODALSHEET_SIZE_NORMAL
        Log(" built a normal size on desktops...")

    End If          
   
''......

Simple, but effective....
These simple tricks help our ABM apps function (and look) better on small devices.
 
Last edited:

Harris

Expert
Licensed User
Longtime User
UI Design Tricks

With large desktop screens, you have the freedom to express anything you want. When you try explain the same on a small device, this becomes a little more challenging in the confined space. So much detail - so little space. Let us allow the user decide what they need to see right now...

This example attempts to use visual cues to help guide your user, in the right direction with an intuitive design of your app.
I admit that I know nothing about this subject - just throwing it out there for your consideration and re-design... (help me out)

None of this is new or revolutionary, it is just a process of discovery using the frameworks and examples from - B4J and ABM.

Card (1)


Enhance the look of CSS - CustomCards. Read This:

Overdue: (Attention man... do something!) Simple inline ABM code...
B4X:
                    If crdate < DateTime.Now Then
                        cbdate = "{B}{I}{C:#FF0000}"&DateTime.Date(crdate)&"{/B}{/I}"
                    Else
                        cbdate = "{B}{C:#184B00}"&DateTime.Date(crdate)&"{/C}{/B}"
                    End If
{C:#FF0000}" - make text RED... Draw attention...


Additional Action Buttons
Determine if this record has notes or attachments...
Rather than polluting the card with this additional info (which could fill many pages), let the user know it is available and decide if they wish to view it...
NOTE: I learned all of this (SQL table handling) by studying the FEEDBACK project included in the ABM download package provided by @alwaysbusy.

Unlike other buttons, you can place action buttons within the view... (top, bottom, left, right with a theme --- sweet...)
In this case, the user is allowed to view attachments or notes - but not edit / delete any...


B4X:
            Dim SQL_str As String = "SELECT * FROM caseattachments WHERE caseACaseID=" & curcase
            Dim attachs As List = DBM.SQLSelect(SQL,SQL_str, Null)

            Dim act2 As ABMActionButton
            If attachs.Size > 0 Then
                Log(" MAKING a attachment button: and place it in the bottom right....)
                act2.Initialize2(page, "a"&curcase, "fa fa-paperclip",  "",  ABM.ACTIONBUTTON_DIRECTION_LEFT,   ABM.ACTIONBUTTON_POSITION_BOTTOMRIGHT,   0,  inset, "btnamber")
                act2.Tag = tblFields ' connect all the table fields (map) for this record to the tag....  
                act2.MainButton.Size = ABM.BUTTONSIZE_NORMAL ' make the button smaller ...
            End If

Card (2)

Note the Pink Flag Icon.... This is a Main Action Button (ActionButton) with a dual purpose...
This indicates the record has been updated with new info... Draw users attention...

The sub menu (action menu buttons) allows the user to add a new note / attachment or modify the record contents (ie. set the status...) based on user permissions.

Card (3)

ABMLabel... Truncate = True
This prevents a long text statement from spilling over the form...

Force Icon to Right Edge
If we have no notes, but attachments... set icon to the right edge...


B4X:
'        Dim inset As Int = 0 ' set this var in the loop....

 Dim act1 As ABMActionButton
            If notes.Size > 0 Then
                Log(" MAKING a note button: "&curcase)
                inset = 50
                act1.Initialize2(page, "n"&curcase, "fa fa-sticky-note-o",  "",  ABM.ACTIONBUTTON_DIRECTION_LEFT,   ABM.ACTIONBUTTON_POSITION_BOTTOMRIGHT,  0, 0, "btncyan")
                act1.Tag = tblFields
                act1.MainButton.Size = ABM.BUTTONSIZE_NORMAL
            End If
            
                
            Dim SQL_str As String = "SELECT * FROM caseattachments WHERE caseACaseID=" & curcase
            Dim attachs As List = DBM.SQLSelect(SQL,SQL_str, Null)

            Dim act2 As ABMActionButton
            If attachs.Size > 0 Then
                Log(" MAKING a attachment button: "&curcase)
                act2.Initialize2(page, "a"&curcase, "fa fa-paperclip",  "",  ABM.ACTIONBUTTON_DIRECTION_LEFT,   ABM.ACTIONBUTTON_POSITION_BOTTOMRIGHT,   0,  inset, "btnamber")
                act2.Tag = tblFields
                act2.MainButton.Size = ABM.BUTTONSIZE_NORMAL
            End If






 

Harris

Expert
Licensed User
Longtime User
A Trap - Error in these Examples...

When developing web server apps, there are things you need to be aware of. Unlike local PC apps (installed on each PC), web apps (a local browser and remote server) throw in a wrench that may make your head spin... until you understand what is happening behind the scenes... Experts already know this where beginners may not (and those like me who can forget lessons once learned).

For Example:


The image below shows a CustomCard viewed on a desktop - where it should have been an ABMTable (wrong result expected).
Q. How did this happen?
A. Using a public var in a shared code module (server side) - (these were removed...)

Yes, I made a big mistake.
Many years ago, @alwaysbusy showed the errors in my ways (of which I was unaware of - at the time) - yet here I go again - repeating the same error...
There is only one @Erel, and then there are the rest of us - struggling to maintain what we have learned / been taught...

Anyways, as I understand it, the problem stems from using a public var in the shared code module.
This can be reset (the server public var) by / from any device (user) at anytime - potentially producing the wrong result.
During my tests of debugging my example app using a phone, tablet and desktop, I discovered this issue.
This removed the public var and returns a current device size...

Proper code sub...

B4X:
 Public Sub SmallDevice(pg As ABMPage) As Boolean

    If (pg.IsPhone) Or (pg.IsTablet) Then
        Return True
        ' pg.ws.Session.SetAttribute("smalldevice", True) ' alternate method as explained below
    Else
        Return False
        ' pg.ws.Session.SetAttribute("smalldevice", False)
    End If

' NOTE: You could also set a local browser Session Variable and accomplish the same thing... (possibly preferd..)
' pg.ws.Session.SetAttribute("smalldevice", True)

'   Then, on each page, determine what device this app is running on by:

'   Class Globals on each page:
'    Private IsSmallDevice as Boolean  = False

'   Set this in the ConnectPage() sub on each
'    IsSmallDevice = session.GetAttribute2("smalldevice",  True) ' give a default value. doesn't hurt (often)

'  Example in each sub on the same page:
'   If IsSmallDevice Then
'        inp.Size = ABM.MODALSHEET_SIZE_FULL
'    Else
'        inp.Size = ABM.MODALSHEET_SIZE_LARGE
'    End If


End Sub
B4X:

If not using a local session var, call the sub like this:

B4X:
    If ABMShared.SmallDevice(page) Then
        inp.Size = ABM.MODALSHEET_SIZE_FULL
    Else
        inp.Size = ABM.MODALSHEET_SIZE_LARGE
    End If

NOTE: PM me with your comments and corrections and I will update this post.



 

Harris

Expert
Licensed User
Longtime User
Modal Sheet Footers (save, cancel buttons, etc...)

Text Buttons do not present well on small devices due to the limited screen width (as compared to a desktop view).
Thankfully, we have "Floating Buttons" which only present an icon. This option is always better when your app is directed at many different languages - where icons are universally understood.

Here we present 3 different views...

Desktop
Our modal sheet footer provides Text for the FLAT buttons... - perfectly fine....




Tablet

Here we use a Floating Button.
Normally, these buttons are Round and rather small in size (makes it hard to touch - but not here fortunately).



Phone

The Phone icons look slightly different - more compressed ( than tablet) due to a narrower screen width. Acceptable... (ignore the text lablels I input with MS Paint)




How can we accomplish this is a simple fashion?
Read the code block below.

Of course, you can make this much "prettier" on different device sizes and widths (portrait vrs landscape), buts that up to you and your design with params / vars.
I only attempt to give you the baisic concepts here without getting to complex..

Modal Sheet Footer Section - (when Building the Sheet):

B4X:
    ' ***************************************
    ' Here we create 1 footer row with 4 cells... (.AddCellsOSMP(4, ...  )
    ' On all devices we create 4 cells ( 3,3,3 ) to hold our buttons
    ' Again, on all devices we will pad the left and right with "15px" - ( 15,  15, "")
    ' Add a theme if you wish - here, none

    inp.Footer.AddRowsM(1, True,0, 0, "").AddCellsOSMP(4, 0,0,0,  3,3,3, 0, 0, 15,  15, "")
    inp.Footer.BuildGrid 'IMPORTANT once you loaded the complete grid AND before you start adding components
    ' ***************************************

    ' ***************************************
    ' In ConnectPage (NOT Build Page), where we Create our Modal Input Sheet, we determine our device size...
    ' ***************************************

    If ABMShared.SmallDevice(page) Then  ' if tablet or phone....

        ' ***************************************
        ' Create floating button(s) with icons - and put it into a cell.
        ' Set the cell to consume the full width of the control (button)
        ' This is where the CELL "padding" comes into effect (15px)
        ' On different device widths (tablet / phone), this will stretch the button look...
        ' ***************************************

        Dim AttBtn As ABMButton
        AttBtn.InitializeFloating( page, "AttBtn", "fa fa-paperclip", "amber")
        inp.Footer.Cell(1,1).AddComponent(AttBtn)
        AttBtn.UseFullCellWidth = True
     
        Dim NoteBtn As ABMButton
        NoteBtn.InitializeFloating(page,  "NoteBtn", "fa fa-sticky-note-o", "amber")
        inp.Footer.Cell(1,2).AddComponent(NoteBtn)
        NoteBtn.UseFullCellWidth = True

        Dim SaveBtn As ABMButton
        SaveBtn.InitializeFloating(page,  "SaveBtn",  "fa fa-check-circle", "green")
        inp.Footer.Cell(1,3).AddComponent(SaveBtn)
        SaveBtn.UseFullCellWidth = True


        Dim CancelBtn As ABMButton
        CancelBtn.InitializeFloating(page,  "CancelBtn", "fa fa-times-circle", "red")
        inp.Footer.Cell(1,4).AddComponent(CancelBtn)
        CancelBtn.UseFullCellWidth = True
     
    Else ' a desktop - big screen...
     
        ' ***************************************
        ' Desktop - create Flat buttons with text...
        ' ***************************************
     
        Dim AttBtn As ABMButton
        AttBtn.InitializeFlat(page,  "AttBtn", "","", "Attach", "")
        inp.Footer.Cell(1,1).AddComponent(AttBtn)
     
        Dim NoteBtn As ABMButton
        NoteBtn.InitializeFlat(page,  "NoteBtn", "","", "Notes", "")
        inp.Footer.Cell(1,2).AddComponent(NoteBtn)

        Dim SaveBtn As ABMButton
        SaveBtn.InitializeFlat(page,  "SaveBtn", "","", "Save", "")
        inp.Footer.Cell(1,3).AddComponent(SaveBtn)

        Dim CancelBtn As ABMButton
        CancelBtn.InitializeFlat(page,  "CancelBtn", "","", "Cancel", "")
        inp.Footer.Cell(1,4).AddComponent(CancelBtn)

    End If


These little tidbits, I contribute in hopes it will tear down the learning curve of making your apps more effective and presentable - using ABM.
There are so many options / params - it takes much time and experimentation in figuring it all out. My retention capacity is limited... (as with many of us).
That said - I still dread the alternative - where I have no capacity to accomplish even 1 percent of what I can comprehend / retain and accomplish here - with ABM.

It still behooves me that someone, in this age of money grabbing and greed, would contribute something (expending much effort) so valuable without any expectation of anything in return? Yet - during the pandemic - our appriciation for each others assistance ( I have noticed ) is getter much better (world wide).
At least there is one bright side from what we have all gone thru...

Thanks
 
Last edited:
Cookies are required to use this site. You must accept them to continue using the site. Learn more…