Roku Developer Program

Join our online forum to talk to Roku developers and fellow channel creators. Ask questions, share tips with the community, and find helpful resources.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
agmark
Visitor

Re: roscreen grid

I want to chime in and thank all of you for contributing to this discussion and sharing your code! It's something I've wanted to explore but haven't had the time to start learning everything required to put a useful roscreen together to display actual content. There's a lot to learn and I'm not sure my old brainbox can keep up, but I'm hoping to dive into it sometime soon 🙂 I can't imaging attempting to learn it without having resources from you guys so thanks for sharing. It's pretty cool stuff so I hope I can follow along and I hope to see more of this developed. It's really useful so thank you.
0 Kudos
NewManLiving
Visitor

Re: roscreen grid

Well they are probably going to kick me off the forum for this one but I took out as much code as possible. This is an example of a virtual grid with a virtual
row buffer and each row of columns have their own virtual column buffer. It is actually a combo of two other components given here and in wisdom. I do not
perceive a great desire for people to learn the api so I limited my commenting. If anyone is interested then I will be glad to answer. Someone requested a complete grid so I am giving this example. Just copy all 1400 or so lines of code into a project and run it. As always it is self contained. It really is a very nice example of not only
a fast grid but some interesting rgb functions at the bottom. EnTerr if you are reading, Thank you for your HexToInteger version, it really is quite nice. Also, if
you could have a cursory look at the other rgb and shift functions at the bottom I would appreciate it. Most of the shift/rgb code I pinched from other code forums but I did combine
a number of them together . It works well to produce random skins for the example. Briefly. The row at the top has focus. the up/down remote keys put you in
row mode where you can also use the paging keys to speed things up. The left/right put you in column mode and that works the same way with the paging keys. Going
up/down puts you back in row mode. It is a lot of code. I had to pull my own code apart since my versions have a lot more functionality and animation. They also
are locked into a data cache manager. But I did test it as it is written and it seems pretty solid. If there is a problem let me know, it would just be a matter of
consulting my own versions to see where the problem is. Anyway just a study of it will give you enough information to fully understand most the important parts of the API






Function Main() As Void

m.Channel = NewChannel()
m.Channel.Initialize()

m.Channel.Compositor.SetDrawTo( m.Channel.Screen, m.Channel.ScrBkgClr )

VirtualGridTest()

m.Channel.Clear()
m.Channel = Invalid

End Function

' ASSERT is used only to short-circuit my own exception handler
' It is only meant for development testing and example code, NOT production
' It would be impossible to bring in my entire framework for one example
' Exception handling is your framework's job. The listview expects data
' to be in order. It does some minor checking for you but that is it
Function ASSERT(a_boolean As Boolean, l_object = "" As String ) As Void

if not a_boolean

print "=========================="
print l_object+" Failed ASSERT"
print "=========================="

stop
end if

End Function

' ************** APPLICATION OBJECT ***************************
' I use this to emulate my own environment. To make is easier for me to
' Bring in code from my own production application.
Function NewChannel() As Object

nc = CreateObject("roAssociativeArray")

' I use one roScreen swapping in/out composites to emulate stacked screens
' and one port as I employ the roSystemLog component.
nc.Screen = Invalid
nc.Compositor = Invalid

nc.Port = Invalid
nc.Device = Invalid
nc.FontRegistry = Invalid

nc.Font20 = Invalid
nc.Font28 = Invalid

nc.DisplayWidth = 0
nc.DisplayHeight = 0

' Default Colors
nc.Transparent = &h00000000
nc.ScrBkgClr = &hE0DFDFFF
nc.DlgBkgClr = &hCECECEFF
nc.ItemTxtClr = &h383838FF

' Remote (you can use bslCore for these if you like)
nc.kp_BK = 0
nc.kp_UP = 2
nc.kp_DN = 3
nc.kp_LT = 4
nc.kp_RT = 5
nc.kp_OK = 6
nc.kp_RW = 7
nc.kp_REV = 8
nc.kp_FWD = 9
nc.kp_INFO = 10

nc.MIN_CELL_WIDTH = 100
nc.MIN_CELL_HEIGHT = 100
nc.MAX_CELL_WIDTH = 300
nc.MAX_CELL_HEIGHT = 300

nc.MIN_ROW_MARGIN = 25
nc.MAX_ROW_MARGIN = 55
nc.MIN_COL_MARGIN = 0
nc.MAX_COL_MARGIN = 25
nc.AVG_COL_MARGIN = 15

nc.Initialize = channel_initialize

' can include interface to create the default
' roCompositor with your overhang at the top
return nc

End Function


Function channel_initialize() As Void

m.Port = CreateObject("roMessagePort")
m.Device = CreateObject("roDeviceInfo")
m.FontRegistry = CreateObject("roFontRegistry")

m.Font20 = m.FontRegistry.GetDefaultFont(20, False, False)
ASSERT(type(m.Font20) = "roFont", "m.Font20")

m.Font28 = m.FontRegistry.GetDefaultFont(28, False, False)
ASSERT(type(m.Font28) = "roFont", "m.Font28")

m.Compositor = CreateObject("roCompositor")
ASSERT(type(m.Compositor) = "roCompositor", "m.Compositor")

m.Screen = CreateObject("roScreen", True)
ASSERT(type( m.Screen) = "roScreen", "m.Screen")

l_wh = m.Device.GetDisplaySize()
m.DisplayWidth = l_wh.w
m.DisplayHeight = l_wh.h

m.Screen.SetPort(m.Port)
m.Screen.Clear(m.ScrBkgClr)
m.Screen.SwapBuffers()

End Function



'************************* Testing Module ***********************************************
Function VirtualGridTest() As Void

config = NewGridConfig()

test_values = CreateObject("roArray", 12, False)

test_values.Push( GetVCBitmaps(10, "A", 224, 200) )
test_values.Push( GetVCBitmaps(10, "B", 224, 200) )
test_values.Push( GetVCBitmaps(10, "C", 224, 200) )
test_values.Push( GetVCBitmaps(10, "D", 224, 200) )
test_values.Push( GetVCBitmaps(10, "E", 224, 200) )
test_values.Push( GetVCBitmaps(10, "F", 224, 200) )
test_values.Push( GetVCBitmaps(10, "G", 224, 200) )
test_values.Push( GetVCBitmaps(10, "H", 224, 200) )
test_values.Push( GetVCBitmaps(10, "I", 224, 200) )
test_values.Push( GetVCBitmaps(10, "J", 224, 200) )
test_values.Push( GetVCBitmaps(10, "K", 224, 200) )
test_values.Push( GetVCBitmaps(10, "L", 224, 200) )

config.Data = test_values
config.RowPageSize = 3
config.ColPageSize = 5
config.RowMargin = 40
config.ColumnMargin = 20
config.CellWidth = 224
config.CellHeight = 200


VGrid = NewVGrid( m.Channel.Compositor )

if VGrid.Set( config )

VGrid.MoveTo(40, 20)
VGrid.Show()
VGrid.EventLoop()

end if

config.Clear()
config = Invalid

VGrid.Clear()
VGrid = Invalid

return

End Function


' Simple configuration and container classes/structures which have no interator interface -
' removes levels of indirection and keeps things easier to manage and read.
' Indexing is handled by the grid members *count *index

' ****************** GRID CONFIGURATION ************************************

Function NewGridConfig() As Object

gc = CreateObject("roAssociativeArray")

' Pointer to array of arrays of bitmaps
gc.Data = Invalid

gc.BkgClr = m.Channel.ScrBkgClr

' Desired maximum row page size (number of child grid rows visible on one page)
gc.RowPageSize = 5
' Desired maximum Column page size for the child gridrows (number of visible columns)
gc.ColPageSize = 5
' Default Grids cell width and height
gc.CellWidth = m.Channel.MIN_CELL_WIDTH
gc.CellHeight = m.Channel.MIN_CELL_HEIGHT

' Row and column margins
gc.RowMargin = m.Channel.MIN_ROW_MARGIN
gc.ColumnMargin = m.Channel.AVG_COL_MARGIN

' Start Index offset of what grid row to display at the top
gc.StartIndex = 0


gc.Reset = Function()

m.Data = Invalid

m.BkgClr = m.Channel.ScrBkgClr

m.RowPageSize = 5
m.ColPageSize = 5
m.CellWidth = m.Channel.MIN_CELL_WIDTH
m.CellHeight = m.Channel.MIN_CELL_HEIGHT

m.RowMargin = m.Channel.MIN_ROW_MARGIN
m.ColumnMargin = m.Channel.AVG_COL_MARGIN

End Function

return gc

End Function


' *********************** GRID CONTAINER *************************************************************

Function NewGridContainer() As Object

gc = CreateObject("roAssociativeArray")

gc.Rows = Invalid

gc.Set = Function( a_rowCount As Integer, a_rowHeight As Integer )

m.Reset()

m.Rows = CreateObject( "roArray", a_rowCount, False )

l_x = 0
l_y = 0

for l_i = 0 to a_rowCount - 1

m.Rows.Push( NewGridRow( l_x, l_y ) )

l_y = l_y + a_rowHeight

end for

End Function


gc.Reset = Function()

if m.Rows <> Invalid

for each l_row in m.Rows : l_row.Clear() : end for

end if

m.Rows = Invalid

End Function

return gc

End Function


Function NewGridRow( a_x As Integer, a_y As Integer ) As Object

gc = CreateObject("roAssociativeArray")

gc.VGCRow = NewVGridCRow()

gc.X = a_x
gc.Y = a_y

return gc

End Function

' ********************************* DATA CONTAINER ************************************
' A container for the data sent to the grid. Since rows are virtual each child row needs
' a way to remember where it left off when it is reloaded into the virtual row
Function NewVDataContainer() As Object


dc = CreateObject("roAssociativeArray")

dc.Data = Invalid

dc.Set = Function( a_dataArray As Object )

m.Reset()

m.Data = CreateObject( "roArray", a_dataArray.Count(), False )

for each l_items in a_dataArray

m.Data.Push( NewDataObject( l_items ) )

end for

End Function


dc.Reset = Function ()

if m.Data <> Invalid then m.Data.Clear()

m.Data = Invalid

End Function


return dc

End Function


Function NewDataObject( a_items As Object ) As Object

do = CreateObject("roAssociativeArray")

do.Items = a_items
do.StartIndex = 0

return do

End Function

' *****************************************************************



' ********************************* VIRTUAL GRID OBJECT ************************************
' This is an extracted, and simplified version of one of my grids. I tend more to use
' row based grids than column based. However, since someone was interested here is the virtual beast
' That has both row and column virtual buffers. In this grid the buffers are one column size for the
' columns, and one row size for the rows. I have other grids that have multiple buffers. But this one
' can load data very fast IF..... you can supply it. At is largest size it consumes < 6 MB of memory but
' is able to handle all the bitmaps you can feed it. That is where the problem lies. To feed this type of
' beast you need to have a very robust data caching system which involves both memory and disk. This grid
' example is not tied in with any data engine as there is just too much code and complexity involved. It is an example
' of a virtual grid. My own method of development uses a more partioned or layered style, that way data is
' introduced in logical sequences. A perfect example is the listview/ row-based grid combo
' I use one screen, multiple compositors (only one is needed for each module implemented)
' But components need to know their parents screen, compositor, and the zorder to place themselves
' I also employ one port as I use the roSystemLog component. To implement this style of grid it is absolutely
' necessary to use sprites as you will see in the code. And, to use sprites, you need a compositor. Trust me
' once you get used to "Compositor, bitmaps, regions, sprites" concept, you will never go back to the old way. There is
' so much more power, speed and a ton of animation fun with the regions. You know, when you have a grid like this
' and the user has to wait a few seconds or more to load other pages, - you just give them something to look at. My grids
' load with the cell data flying, rotating in, or fading in while my custom cursors are providing a side-show
' of their own. I call this the Illusion of Speed. But you cannot do this type of complexity with just an roScreen
' and some bitmaps. You just get a sluggish mess. Bottom line, I'm putting it out as is. Many things are fixed
' as time and space does not permit and honestly, I do not think very many people are interested in the API. There
' is alot you can do. My own grids have dynamic columns that can slide into center or anywhere else and the selector knows
' which cells have data and which are still loading or do not have data (invisible cells) the selector simply glides
' over these areas. You will see all this if I ever get my own application out. If you have any questions
' you can always ask. But this is a lot of work for a few interested people. So the commenting is limited. Most
' of it is expalined in other examples

' How this works: The top row is the only row that scrolls column wise. There are two modes. The row mode
' and the column mode. To move from row to row you use the up down arrow keys and the paging keys. using the
' left right keys puts you in column mode usng the paging keys in this mode moves the columns very quickly.
' Resuming with up/down controls puts you back in row mode. Flat paging of data has been left out for size
' Regions. This is the third mode which is not included in this example. If you want it just ask

' Most of this code is explained in depth in the wisdom and roscreen grid threads. Where it is not I will explain
Function NewVGrid( a_compositor As Object, a_zOrder = 1 As Integer ) As Object

vg = CreateObject("roAssociativeArray")

vg.Channel = m.Channel
vg.Screen = m.Channel.Screen
vg.Port = m.Channel.Port
vg.Compositor = a_compositor

' The Zorder to place the listview
vg.ZOrder = a_zOrder

' Default Colors, fonts
vg.BkgClr = m.Channel.ScrBkgClr
vg.TextFont = m.Channel.Font28
vg.TextClr = m.Channel.ItemTxtClr

' A child row configuatoin structure which is
' used for the virtual row
vg.Config = Invalid

' As stated above I keep the structures simple as I am not sure
' how optimized indirection is in BrightScript. I only read the docs when I have to.
' The GridContainer
vg.GC = Invalid
vg.Rows = Invalid
vg.RowCount = 0
vg.RowIndex = 0

' The Data Container
vg.DC = Invalid
vg.Data = Invalid
vg.DataCount = 0
vg.DataIndex = 0

' What row to start with
vg.StartIndex = 0

' Number of grid rows to display
vg.RowPageSize = 0
' Number of grid columns to display
vg.ColPageSize = 0

' RowHeight * RowPageSize
vg.PageHeight = 0
vg.PageNumber = 0
vg.TotalPages = 0

vg.IsPartialPage = False
vg.IsPagingDown = True

' Cell Height + Row Margin
vg.RowHeight = 0
vg.RowMargin = 0

' Cell Width + Column Margin
vg.ColumnWidth = 0
vg.ColumnMargin = 0
vg.CellWidth = 0
vg.CellHeight = 0

' The bitmap for the child rows to draw into
'
vg.BufferBitmap = Invalid
vg.BufferRegion = Invalid
vg.BufferSprite = Invalid

' Always dynamically calculated
vg.BufferWidth = 0
vg.BufferHeight = 0
vg.RegionWidth = 0
vg.RegionHeight = 0

' PUBLIC
vg.Set = virtual_grid_set
vg.Show = virtual_grid_show
vg.Hide = virtual_grid_hide
vg.MoveTo = virtual_grid_moveto
vg.GetWidth = virtual_grid_getwidth
vg.GetHeight = virtual_grid_getheight
vg.ScrollRow = virtual_grid_scrollrow

' The event loop can be used or you can control the grid yourself
' with the above functions
vg.EventLoop = virtual_grid_eventloop

' PROTECTED
vg.DrawPage = virtual_grid_drawpage
vg.DrawAll = virtual_grid_drawall
vg.Reset = virtual_grid_reset

return vg

End Function

' Releases any sprite memory and resets all members to default
Function virtual_grid_reset() As Void

if m.BufferSprite <> Invalid then m.BufferSprite.Remove()
if m.GC <> Invalid then m.GC.Reset()
if m.DC <> Invalid then m.DC.Reset()

m.GC = Invalid
m.Rows = Invalid
m.RowCount = 0
m.RowIndex = 0

m.DC = Invalid
m.Data = Invalid
m.DataCount = 0
m.DataIndex = 0

m.TextFont = m.Channel.Font28
m.TextClr = m.Channel.ItemTxtClr
m.BkgClr = m.Channel.ScrBkgClr

m.Config = Invalid

m.StartIndex = 0

m.RowPageSize = 0
m.ColPageSize = 0

m.PageHeight = 0
m.PageNumber = 0
m.TotalPages = 0
m.IsPagingDown = True
m.IsPartialPage = False

m.RowHeight = 0
m.RowMargin = 0

m.ColumnWidth = 0
m.ColumnMargin = 0
m.CellWidth = 0
m.CellHeight = 0

m.BufferBitmap = Invalid
m.BufferRegion = Invalid
m.BufferSprite = Invalid
m.BufferWidth = 0
m.BufferHeight = 0
m.RegionWidth = 0
m.RegionHeight = 0

return

End Function

' Sets up the grid
Function virtual_grid_Set( a_config As Object ) As Boolean

' Release resources and re-initialize
m.Reset()

' Assign the configuration data
m.RowPageSize = a_config.RowPageSize
m.ColPageSize = a_config.ColPageSize
m.StartIndex = a_config.StartIndex
m.CellWidth = a_config.CellWidth
m.CellHeight = a_config.CellHeight
m.RowMargin = a_config.RowMargin
m.ColumnMargin = a_config.ColumnMargin
m.BkgClr = a_config.BkgClr

' Normalize the data/count use your own error system. I do not even have returns. I'm from the old school
' Create a debug version using exception handlng, throws, asserts and a production version free of the nonsense
' I just make sure what I am 'setting' is accurate
l_dataCount = a_config.Data.Count()
if l_dataCount = 0 then return False

if m.RowPageSize >= l_dataCount then m.RowPageSize = l_dataCount - 1
if m.RowPageSize <= 0 then m.RowPageSize = 1

' Total number of pages if the code above is correct then
' This type of paging is not shown in this example
m.TotalPages = int( l_dataCount / m.RowPageSize )
m.IsPartialPage = ( l_dataCount MOD m.RowPageSize ) > 0
if m.IsPartialPage then m.TotalPages = m.TotalPages + 1

' Sizes are fixed for the examle, but generous. And I have not tested all combos
' You get it as is. My own production code is far more complex so I have
' to pick and pull out things and don't have time to test everytning. But if you find something
' or need something I will fix or provide
if m.CellWidth < m.Channel.MIN_CELL_WIDTH then m.CellWidth = m.Channel.MIN_CELL_WIDTH
if m.CellWidth > m.Channel.MAX_CELL_WIDTH then m.CellWidth = m.Channel.MAX_CELL_WIDTH

if m.CellHeight < m.Channel.MIN_CELL_HEIGHT then m.CellHeight = m.Channel.MIN_CELL_HEIGHT
if m.CellHeight > m.Channel.MAX_CELL_HEIGHT then m.CellHeight = m.Channel.MAX_CELL_HEIGHT

if m.RowMargin < m.Channel.MIN_ROW_MARGIN then m.RowMargin = m.Channel.MIN_ROW_MARGIN
if m.RowMargin > m.Channel.MAX_ROW_MARGIN then m.RowMargin = m.Channel.MAX_ROW_MARGIN

if m.ColumnMargin < m.Channel.MIN_COL_MARGIN then m.ColumnMargin = m.Channel.MIN_COL_MARGIN
if m.ColumnMargin > m.Channel.MAX_COL_MARGIN then m.ColumnMargin = m.Channel.MAX_COL_MARGIN

' Calculate total buffer width
' Adjust for out-of-bounds
m.ColumnWidth = m.CellWidth + m.ColumnMargin
l_maxWidth = m.Channel.DisplayWidth + m.ColumnWidth
l_buffWidth = m.ColPageSize * m.ColumnWidth
if l_buffWidth > l_maxWidth then m.ColPageSize = int( l_maxWidth / m.ColumnWidth )

' Total buffer width including the virtual cell (col page size is actually the child row grid size)
m.BufferWidth = m.ColPageSize * m.ColumnWidth + m.ColumnWidth

' Calculate total buffer height
m.RowHeight = m.CellHeight + m.RowMargin
l_maxHeight = m.Channel.DisplayHeight + m.RowHeight
l_buffHeight = m.RowPageSize * m.RowHeight
if l_buffHeight > l_maxHeight then m.RowPageSize = int( l_maxHeight / m.RowHeight )

' The buffer height - and a row height for the virtual buffer
m.BufferHeight = m.RowPageSize * m.RowHeight + m.RowHeight

' Finally, create the bitmap buffer
m.BufferBitmap = CreateObject("roBitmap", {width: m.BufferWidth, height: m.BufferHeight , AlphaEnable: False})
if m.BufferBitmap = Invalid then return False
m.BufferBitmap.Clear( m.BkgClr )

' Page width , region width (cut off the end margin for even scrolling)
m.PageHeight = m.RowPageSize * m.RowHeight
m.RegionWidth = m.BufferWidth
m.RegionHeight = m.PageHeight - m.RowMargin

m.BufferRegion = CreateObject("roRegion", m.BufferBitmap, 0, 0, m.RegionWidth, m.RegionHeight)
if m.BufferRegion = Invalid then return False
' Set the regions wrap to true for scrolling. This must always be done
m.BufferRegion.SetWrap(True)

' The Interface members show, moveto, will handle this later
m.BufferSprite = m.Compositor.NewSprite(0, 0, m.BufferRegion, -1)
if m.BufferSprite = Invalid then return False

' Create the grid definition container m.RowPageSize + 1 for virtual row
' Initialize a pointer to the rows member for easier access
' Set the count and start index. I use the grid to do this for speed. I am not sure how
' optimal indirection is as there is no true oo in brightscript - the lmited m pointer is a clear example of this
m.GC = NewGridContainer()
m.GC.Set( m.RowPageSize + 1, m.RowHeight )

m.Rows = m.GC.Rows
m.RowCount = m.Rows.Count()
m.RowIndex = 0

' Set the containers data member and hold on to another
' reference in the grids data member for easier access
' Get the total count and set the start index

m.DC = NewVDataContainer()
m.DC.Set( a_config.Data )

m.Data = m.DC.Data
m.DataCount = m.Data.Count()

' Create the grid cell configuration object and call set on each
' of the child rows except the virtual row which will contain
' a child row with no data set at this time

m.Config = NewGridConfig()

' Each child will draw onto the parents bitmap
' As instructed by the parent grids settings
m.Config.Compositor = m.Compositor
m.Config.BufferBitmap = m.BufferBitmap
m.Config.CellWidth = m.CellWidth
m.Config.CellHeight = m.CellHeight
m.Config.PageSize = m.ColPageSize
m.Config.Margin = m.ColumnMargin
m.Config.BkgClr = m.BkgClr
m.Config.X = 0
m.Config.Y = 0

' Set each childs data and x, y pos in the grid
for l_i = 0 to m.RowPageSize - 1

m.Config.Data = m.Data[l_i]

l_vgcrow = m.Rows[l_i].VGCRow
l_vgcrow.Set( m.Config )

m.Config.Y = m.Config.Y + m.RowHeight

end for

m.DataIndex = l_i - 1

m.DrawPage()

return True

End Function


' Instructs the compositor to draw all sprites and then the screen swaps out the back buffer
' Dont need to call Flush it is called for you in SwapBuffers
Function virtual_grid_drawall() As Void

m.Compositor.DrawAll()
m.Screen.SwapBuffers()

End Function

' Move the sprite to where you like. The drawall arg allows you to stack drawing before dumping
' And Yes it will fail if objects are invalid. Use the assert or a debug version
Function virtual_grid_Moveto(a_x = 0 As Integer, a_y = 0 As Integer, a_isDrawAll = False As Boolean ) As Void

m.BufferSprite.MoveTo(a_x, a_y)

if a_isDrawAll then m.DrawAll()

End Function

' Show it
Function virtual_grid_show(a_isDrawAll = True As Boolean) As Void

m.BufferSprite.SetZ(m.ZOrder)

if a_isDrawAll then m.DrawAll()

End Function


Function virtual_grid_hide(a_isDrawAll = True As Boolean) As Void

m.BufferSprite.SetZ(-1)

if a_isDrawAll then m.DrawAll()

End Function

Function virtual_grid_getwidth() As Integer

return m.BufferRegion.GetWidth()

End Function


Function virtual_grid_getheight() As Integer

return m.BufferRegion.GetHeight()

End Function


' Draws the complete page starting at y pos 0 in the bitmap buffer. DataIndex is calculated in the
' virtual_grid_scrollpage handler. Each child row is told to draw itself within the parent bitmap buffer
' This member does not wirte to the the virtual row, but it always resets the virtual index pointer
' To the last row in the buffer. When a page is drawn paging down is always true
Function virtual_grid_drawpage() As Void

for l_i = 0 to m.RowPageSize - 2

m.Rows[l_i].VGCRow.DrawPage()

end for

m.Rows[l_i].VGCRow.DrawPage()

m.RowIndex = l_i
m.IsPagingDown = True

End Function

' When in row mode this function setsup the indexes, writes to the virtual row and then scrolls it into
' the viewable region. Actually is offsets the region which is the reason for most of the complexity
Function virtual_grid_scrollrow( a_isDown = True As Boolean, a_frames = 16 As Integer ) As Void

' If user transitions from scrolling up/down you must adjust the dataindex and virtualindex
' by a page size since the indexes are either at the top or bottom of the visible region
' depending on the direction the user is moving. You can then resume row scrolling in the
' new direction

' In this case, the user wants to move downward
if a_isDown

' Transition adjustment made proceed with row-based indexing
if m.IsPagingDown

m.DataIndex = m.DataIndex + 1
if m.DataIndex >= m.DataCount then m.DataIndex = 0

m.RowIndex = m.RowIndex + 1
if m.RowIndex >= m.RowCount then m.RowIndex = 0

else

' User just changed directions
' So both indexes have to be bumped up a page. There are other ways to do this
' in my past experience I have seen indexes which keep track of the top and bottom rows
' I just offset and correct

' Data Index
m.DataIndex = m.DataIndex + m.RowPageSize
if m.DataIndex >= m.DataCount then m.DataIndex = m.DataIndex - m.DataCount

m.RowIndex = m.RowIndex + m.RowPageSize
if m.RowIndex >= m.RowCount then m.RowIndex = m.RowIndex - m.RowCount

' Adjustment made, set the flag to true
m.IsPagingDown = True

end if

else
' The user is now scrolling down and the transition has been made proceed row-based indexing
if not m.IsPagingDown

m.DataIndex = m.DataIndex - 1
if m.DataIndex < 0 then m.DataIndex = m.DataCount - 1

m.RowIndex = m.RowIndex - 1
if m.RowIndex < 0 then m.RowIndex = m.RowCount - 1

else
' Need to transition and turn the flag off - note that you are adding a negative number here
' if < 0
m.DataIndex = m.DataIndex - m.RowPageSize
if m.DataIndex < 0 then m.DataIndex = m.DataCount + m.DataIndex

m.RowIndex = m.RowIndex - m.RowPageSize
if m.RowIndex < 0 then m.RowIndex = m.RowCount + m.RowIndex

m.IsPagingDown = False

end if

end if

' Since the virtual row is not fixed as the region moves but the bitmap is stationary
' and the region wraps around you are always keeping ahead of the region like a dog chasing
' its tail. Get the row below or above the region (depending on which direction)
' the get the data at the data containers index as also set above. Then set that childs
' data to reflect the new index. Then call the childs draw page to write to the bitmap
l_row = m.Rows[ m.RowIndex ]

m.Config.Data = m.Data[ m.DataIndex ]
m.Config.X = l_row.X
m.Config.Y = l_row.Y

l_row.VGCRow.Set( m.Config )
l_row.VGCRow.DrawPage()

' Now just Scroll the row into view
l_offset = 0
l_prevset = 0
l_offdiff = 0

if a_frames > 200 then a_frames = 200
if a_frames < 2 then a_frames = 2

for l_i = 1 to a_frames

l_offset = int(m.RowHeight * l_i / a_frames)
l_offdiff = l_offset - l_prevset

if l_offdiff > 0

if not a_isDown l_offdiff = -l_offdiff

m.BufferRegion.Offset(0, l_offdiff, 0, 0)

m.DrawAll()

l_prevset = l_offset

end if

end for

return

End Function


' This example uses the provided event loops but you can just use the interface
' You can expand upon the configuration object to include speeds and other user initiated handling
Function virtual_grid_eventloop() As Void


l_index = 0
l_lastKey = -1
l_running = True

l_normalSpeed = 180
l_scrollSpeed = l_normalSpeed

l_spriteX = m.BufferSprite.GetX()
l_spriteY = m.BufferSprite.GetY()

l_kp_BK = m.Channel.kp_BK
l_kp_UP = m.Channel.kp_UP
l_kp_DN = m.Channel.kp_DN
l_kp_LT = m.Channel.kp_LT
l_kp_RT = m.Channel.kp_RT
l_kp_REV = m.Channel.kp_REV
l_kp_FWD = m.Channel.kp_FWD
l_kp_OK = m.Channel.kp_OK

while(l_running)

l_msg = wait(l_scrollSpeed, m.Port)

if type(l_msg) = "roUniversalControlEvent"

l_index = l_msg.GetInt()
l_lastKey = l_index

if l_index = l_kp_RT or l_index = l_kp_LT

' Determine the index of the top row by diving the regions current y pos
' by the row height. Recall that in building the grid these are all multiples
' of each other. I am working on a grid that has various size rows.
m.TopIndex = m.BufferRegion.GetY() / m.RowHeight
' Now get the child from the grid container and call its show member
' the return value is the lastkey pressed by the user which to get out of
' column mode would be up or down. The sprite position is not changed here so
' you can just save them as I did above . More on this when you get to the
' show method
l_vgcRow = m.Rows[ m.TopIndex ].VGCRow
l_lastKey = l_vgcRow.Show( l_spriteX, l_spriteY, l_index, m.ZOrder + 1 )

' Determine the lastkey in colum mode if an exit then exit other wise go in
' the user supplied direction
if l_lastKey <> l_kp_BK
m.ScrollRow( l_lastKey = l_kp_DN )
else
l_running = False
end if

else if l_index = l_kp_UP or l_index = l_kp_DN

m.ScrollRow( l_index = l_kp_DN )

else if l_index = l_kp_FWD or l_index = l_kp_REV

m.ScrollRow( l_index = l_kp_FWD )

else if l_index > 100 ' All key ups are value + 100

l_lastKey = -1
l_scrollSpeed = l_normalSpeed

else if l_index = l_kp_BK

l_running = False

else if l_index = l_kp_OK


end if

else

if l_lastKey = l_kp_UP or l_lastKey = l_kp_DN

l_scrollSpeed = 5
m.ScrollRow(l_lastKey = l_kp_DN)

else if l_lastKey = l_kp_FWD or l_lastKey = l_kp_REV

l_scrollSpeed = 5
m.ScrollRow(l_lastKey = l_kp_FWD, 0)

end if

end if

end while


End Function


'********************************** GRID ROW OBJECT AND CONTAINER *************************************
' This is just a stripped down version of the grid row example on the roscreen grid thread
' It is a child of the grid above and is managed by the grid. When in column mode it functions
' independantly
Function NewGridRowContainer() As Object

gc = CreateObject("roAssociativeArray")

gc.Rows = Invalid

gc.Set = Function( a_rows As Integer, a_cols As Integer ) As Void

if m.Rows <> Invalid then m.Reset()

m.Rows = CreateObject("roArray", a_rows, False)

for l_i = 0 to a_rows - 1

l_cols = CreateObject("roArray", a_cols, False)

for l_j = 0 to a_cols - 1

l_cols.Push( NewGridCell() )

end for

m.Rows.Push( l_cols )

end for

End Function


gc.Reset = Function() As Void

if m.Rows <> Invalid

for each l_col in m.Rows : l_col.Clear() : end for

end if

m.Rows.Clear()
m.Rows = Invalid

End Function

return gc

End Function


Function NewGridCell(a_x = 0 As Integer, a_y = 0 As Integer) As Object

gc = CreateObject("roAssociativeArray")

gc.X = a_x
gc.Y = a_x

return gc

End Function



' ************************** GRID ROW OBJECT ***********************************************
Function NewVGridCRow() As Object

gr = CreateObject("roAssociativeArray")

' System application object, screen port
gr.Channel = m.Channel
gr.Screen = m.Channel.Screen
gr.Port = m.Channel.Port

' Ref to implementor's compositor
gr.Compositor = Invalid

' Description of rows, cells and a ref to the one row in the container
gr.GridContainer = Invalid
gr.GridColums = Invalid
gr.GridColCount = 0
gr.GridColIndex = 0

' Holds a reference to your data container.
gr.Data = Invalid
gr.Items = Invalid
gr.ItemCount = 0
gr.ItemIndex = 0

' The paging information.
gr.PageSize = 0

' Flag to alert me that the user has changed scrolling directions
gr.IsPagingDown = True

gr.ColumnWidth = 0
gr.CellWidth = 0
gr.CellHeight = 0
gr.CellMargin = 0

' The parent bitmap that will buffer the data.
gr.BufferBitmap = Invalid
' Reference to the dynamic region and sprite
' That scrolls the list when in focus
gr.BufferRegion = Invalid
gr.BufferSprite = Invalid
' Precalulate for speed
gr.RegionWidth = 0
gr.RegionHeight = 0
gr.X = 0
gr.Y = 0

' Default Colors
gr.BkgClr = m.Channel.ScrBkgClr

' PUBLIC
gr.Set = virtual_gridc_row_set
gr.Show = virtual_gridc_row_show
gr.ScrollCell = virtual_gridc_row_scrollcell
gr.EventLoop = virtual_gridc_row_eventloop

' PROTECTED
gr.DrawPage = virtual_gridc_row_drawpage
gr.DrawAll = virtual_gridc_row_drawall
gr.Reset = virtual_gridc_row_reset

return gr

End Function


' Releases any sprite memory and resets all members to default
Function virtual_gridc_row_reset() As Void

if m.BufferSprite <> Invalid then m.BufferSprite.Remove()
if m.GridContainer <> Invalid then m.GridContainer.Clear()

m.GridContainer = Invalid
m.BufferSprite = Invalid

m.GridColIndex = 0
m.ItemIndex = 0
m.StartIndex = 0

return

End Function


Function virtual_gridc_row_set( a_config As Object ) As Boolean

' Release resources and re-initialize
m.Reset()

m.CellWidth = a_config.CellWidth
m.CellHeight = a_config.CellHeight
m.Margin = a_config.Margin
m.PageSize = a_config.PageSize
m.BkgClr = a_config.BkgClr
m.BufferBitmap = a_config.BufferBitmap
m.Compositor = a_config.Compositor

m.X = a_config.X
m.Y = a_config.Y

' Get a reference to the data object
m.Data = a_config.Data

' Resolve the items member
m.Items = m.Data.Items
m.ItemCount = m.Items.Count()
m.ItemIndex = m.Data.StartIndex

' Cannot have a page size the same as the data size except 1
if m.PageSize >= m.ItemCount then m.PageSize = m.ItemCount - 1
if m.PageSize <= 0 then m.PageSize = 1

' Calculate total column width
m.ColumnWidth = (m.CellWidth + m.Margin)

' Page width , region width (cut off the end margin for even scrolling)
m.RegionWidth = m.PageSize * m.ColumnWidth - m.Margin
m.RegionHeight = m.CellHeight

' Create the grid definition container at one row, m.PageSize + 1 Columns
' Get reference to the members and setup indexing
m.GridContainer = NewGridRowContainer()
m.GridContainer.Set( 1, m.PageSize + 1 )
m.GridColumns = m.GridContainer.Rows[0]
m.GridColCount = m.GridColumns.Count()

' Grid cell x, y positions. Remember that they can change for the virtual row
' set is continually called for any one of the datum that needs to be reloaded but
' ususally at a different location in the bitmap buffer
l_x = m.X
l_y = m.Y

for each l_col in m.GridColumns

l_col.x = l_x
l_col.y = l_y

l_x = l_x + m.ColumnWidth

end for

return True

End Function


Function virtual_gridc_row_drawall() As Void

m.Compositor.DrawAll()
m.Screen.SwapBuffers()

End Function

' Now this is how all this comes together
Function virtual_gridc_row_show( a_sx As Integer, a_sy As Integer, a_index As Integer, a_zorder As Integer ) As Integer

' the child does not create its own sprite or regoin at the time of its creation. It just simply draws itself
' into the parents bitmap until its service are required. This way the parent can simply use its own region to
' scroll the individual child rows when in fact they are just a picture of the current state of the row

' Now we have to kind of lift it off or sprite it above its own reflection.
' You must release any memory allocated for a previous sprite or it will collect
if m.BufferSprite <> Invalid then m.BufferSprite.Remove()

' Now here, recall that the m.X, m.Y members were set in the set function. so we know the x and y
' pos within the bitmap and we also know the width and height that is occupied within the parent bitmap
' All we want to do is create a seperate region and sprite it. What this does in effect is peel it off
' of the bitmap and anitmate it. SetWrap MUST BE TRUE
m.BufferRegion = CreateObject("roRegion", m.BufferBitmap, m.X, m.Y, m.RegionWidth, m.RegionHeight)
m.BufferRegion.SetWrap(True)
' Now just sprite it right above its reflection
m.BufferSprite = m.Compositor.NewSprite( a_sx, a_sy, m.BufferRegion, a_zorder )

' Call the scroll member with the direction indicated by the user (resolves to true or false)
m.ScrollCell( a_index = m.Channel.kp_RT )

' Then go into the event loop with the current key index so that last_key can be setup properly
' Upon return we just hand back the last key to the parent
l_index = m.EventLoop(a_index)

' Now since the sprite is only an illusion riding on top of the contents of the parent
' we need to repaint the parent to reflect the new cell position. Recall than when the user
' moves to the right, the indexes are page flipped so what is seen at the top is not the
' actual index. Howeve, if the user is scrolling left then the indexing is at the top going
' backwards. So if m.IsPagingDown, you have to reset the data index to the first cell displayed
' And then redraw the page
if m.IsPagingDown

m.ItemIndex = m.ItemIndex - m.PageSize + 1
if m.ItemIndex < 0 then m.ItemIndex = m.ItemCount + m.ItemIndex

end if

' Set the start index to the new index so we know where to start the next time the
' row is reset
m.Data.StartIndex = m.ItemIndex

' Now just draw the page at the same column as indicated in the sprite
m.DrawPage()

' Now just remove the sprite, and draw
m.BufferSprite.Remove()
m.BufferSprite = Invalid
m.BufferRegion = Invalid

m.DrawAll()

return l_index

End Function


Function virtual_gridc_row_drawpage() As Void

for l_i = 0 to m.PageSize - 2

l_col = m.GridColumns[l_i]

m.BufferBitmap.DrawObject( l_col.X, l_col.Y, m.Items[m.ItemIndex] )

' Wrap around if needed
m.ItemIndex = m.ItemIndex + 1
if m.ItemIndex >= m.ItemCount then m.ItemIndex = 0

end for

' Draw the final row and set the virtual index, indexes are always at the bottom
' when drawing a page so you just set the flag to true, of course you have to
' visualize the page in column format going right x, while y is fixed at 0
l_col = m.GridColumns[l_i]
m.BufferBitmap.DrawObject(l_col.X, l_col.Y, m.Items[m.ItemIndex])

m.GridColIndex = l_i
m.IsPagingDown = True

End Function


Function virtual_gridc_row_scrollcell(a_isRight = True As Boolean, a_frames = 16 As Integer) As Void

if a_isRight

if m.IsPagingDown

m.ItemIndex = m.ItemIndex + 1
if m.ItemIndex >= m.ItemCount then m.ItemIndex = 0

m.GridColIndex = m.GridColIndex + 1
if m.GridColIndex >= m.GridColCount then m.GridColIndex = 0

else

m.ItemIndex = m.ItemIndex + m.PageSize
if m.ItemIndex >= m.ItemCount then m.ItemIndex = m.ItemIndex - m.ItemCount

m.GridColIndex = m.GridColIndex + m.PageSize
if m.GridColIndex >= m.GridColCount then m.GridColIndex = m.GridColIndex - m.GridColCount

m.IsPagingDown = True

end if

else

if not m.IsPagingDown

m.ItemIndex = m.ItemIndex - 1
if m.ItemIndex < 0 then m.ItemIndex = m.ItemCount - 1

m.GridColIndex = m.GridColIndex - 1
if m.GridColIndex < 0 then m.GridColIndex = m.GridColCount - 1

else

' Otherwise the user was formally paging right so we have to flip and set the flag
m.ItemIndex = m.ItemIndex - m.PageSize
if m.ItemIndex < 0 then m.ItemIndex = m.ItemCount + m.ItemIndex

m.GridColIndex = m.GridColIndex - m.PageSize
if m.GridColIndex < 0 then m.GridColIndex = m.GridColCount + m.GridColIndex

m.IsPagingDown = False

end if

end if

l_col = m.GridColumns[ m.GridColIndex ]
m.BufferBitmap.DrawObject( l_col.X, l_col.Y, m.Items[m.ItemIndex] )

l_offset = 0
l_offdiff = 0
l_prevset = 0

if a_frames < 2 then a_frames = 2
if a_frames > 200 then a_frames = 200

for l_i = 1 to a_frames

l_offset = int(m.ColumnWidth * l_i / a_frames)
l_offdiff = l_offset - l_prevset

if l_offdiff > 0

if not a_isRight then l_offdiff = -l_offdiff

m.BufferRegion.Offset(l_offdiff, 0, 0, 0)

m.DrawAll()

l_prevset = l_offset

end if

end for

End Function


Function virtual_gridc_row_eventloop(a_lastKey = -1 As Integer, a_scrollSpeed = 180 As Integer) As Integer

l_lastKey = a_lastKey
l_scrollSpeed = a_scrollSpeed

l_index = 0
l_running = True
l_normalSpeed = 180

l_kp_BK = m.Channel.kp_BK
l_kp_UP = m.Channel.kp_UP
l_kp_DN = m.Channel.kp_DN
l_kp_LT = m.Channel.kp_LT
l_kp_RT = m.Channel.kp_RT
l_kp_REV = m.Channel.kp_REV
l_kp_FWD = m.Channel.kp_FWD
l_kp_OK = m.Channel.kp_OK

while(l_running)

l_msg = wait( l_scrollSpeed, m.Port )

if type(l_msg) = "roUniversalControlEvent"

l_index = l_msg.GetInt()
l_lastKey = l_index

if l_index = l_kp_RT or l_index = l_kp_LT

m.ScrollCell( l_index = l_kp_RT )
l_scrollSpeed = l_normalSpeed

else if l_index = l_kp_FWD or l_index = l_kp_REV

m.ScrollCell( l_lastKey = l_kp_FWD)

else if l_index > 100 ' All key ups are value + 100

l_lastKey = -1
l_scrollSpeed = l_normalSpeed

else if l_index = l_kp_BK or l_index = l_kp_DN or l_index = l_kp_UP

l_running = False

end if

else

if l_lastKey = l_kp_RT or l_lastKey = l_kp_LT

l_scrollSpeed = 5
m.ScrollCell(l_lastKey = l_kp_RT)

else if l_lastKey = l_kp_FWD or l_lastKey = l_kp_REV

l_scrollSpeed = 5
m.ScrollCell(l_lastKey = l_kp_FWD, 0)

end if

end if


end while

return l_index

End Function

' Your on your own here. Just some test bitmap, too many will sink you
' ***************** Dummy Data ******************************
Function GetVCBitmaps(number As Integer, row As String, width As Integer, height As Integer) As Object

if number > 150 then number = 100

fontRegistry = CreateObject("roFontRegistry")
font32 = fontRegistry.GetDefaultFont(32, True, False)
bitmaps = CreateObject("roArray", number, False)

textHeight = font32.GetOneLineHeight()
y = int(height / 2 - textHeight / 2)

for i = 1 to number

bitmap = CreateObject("roBitmap", {width: width, height: height, AlphaEnable: false} )
bitmap.Clear( HexToInteger( RGBToHexString( RND(200), RND(155), RND(50) ) ) )

i_str = row+" "+i.ToStr()
str_w = font32.GetOneLineWidth(i_str, width)
x = int(width / 2 - str_w / 2)
bitmap.DrawText(i_str, x, y, &hFFFFFFFF, font32)

bitmaps.Push(bitmap)

end for

return bitmaps

End Function

' They are crude but they work for what they are intended for - RGB values
' EnTerr if you are reading your expertise would be greatly appreciated in any
' optimization your brain would come up with. I really like the HexToInteger3
' Although I have a different view on error checking. I put that in debug versions
' But the functions below were put together hastily and most of it came from a few
' trips around google land, actuall some are combinations of solutions other people had
Function RGBtoHexString( a_r As Integer, a_g As Integer, a_b As Integer ) As String

l_rgb = LeftShift( a_r and &hFF, 16 ) or LeftShift( a_g and &hFF, 8 ) or a_b and &hFF

return IntegerToHexString ( l_rgb )

End Function


Function IntegerToHexString( a_number As Integer ) As String

l_hexDigits = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
l_hexString = ""

for l_i = 5 to 0 step -1

l_hexString = l_hexString + l_hexDigits[ RightShift( a_number, l_i * 4 ) and &hF ]

end for

return l_hexString+"FF"

End Function


Function RightShift( a_number As Integer, a_shift As Integer ) As Integer

if a_number > 0 then a_number = Int( a_number / ( 2 ^ a_shift ) )

return a_number

End Function


Function LeftShift( a_number As Integer, a_shift As Integer ) As Integer

if a_number > 0

for l_i = 1 to a_shift

l_number = a_number and &h40000000
a_number = ( a_number and &h3FFFFFFF ) * 2
if l_number then a_number = a_number or &h80000000

end for

end if

return a_number

End Function

' Thanks enTerr
Function HexToInteger( a_hex As String ) As Integer

l_bArr = CreateObject("roByteArray")
l_bArr.FromHexString( a_hex )

l_int = 0
for each l_byte in l_bArr : l_int = 256 * l_int + l_byte : end for

return l_int

End Function














My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )
0 Kudos
EnTerr
Roku Guru

Re: roscreen grid

"NewManLiving" wrote:
EnTerr if you are reading, Thank you for your HexToInteger version, it really is quite nice. Also, if you could have a cursory look at the other rgb and shift functions at the bottom I would appreciate it. Most of the shift/rgb code I pinched from other code forums but I did combine a number of them together .

To your specific question, i think this can be done more succinctly:

function RGBA_to_hex(R, G, B, A):
b = createObject("roByteArray")
b[0] = R 'if >255, assignment trims it (as if "and &hFF")
b[1] = G
b[2] = B
b[3] = A
return b.toHexString()
end function

function RGBA_to_int(R, G, B, A):
return 0h1000000*R + 0h10000*G + 0h100*B + A
end function
On cursory look your code only needs the 2nd one.
0 Kudos
squirreltown
Roku Guru

Re: roscreen grid

Thanks NewManLiving! Thats a really nice grid. Now to figure out how it works! Smiley LOL
Kinetics Screensavers
0 Kudos
NewManLiving
Visitor

Re: roscreen grid

"NewManLiving" wrote:
function RGBA_to_hex(R, G, B, A):
b = createObject("roByteArray")
b[0] = R 'if >255, assignment trims it (as if "and &hFF")
b[1] = G
b[2] = B
b[3] = A
return b.toHexString()
end function

function RGBA_to_int(R, G, B, A):
return 0h1000000*R + 0h10000*G + 0h100*B + A
end function


Thank you sir! That is elegant . So readers even if you are not interested in the grid you should pick up EnTerr's RGBA_* functions and HexToInteger function and put them in your toolbox. You may also want the shift functions until a better version is available. The byteArray is just that ; a byte array: so that is obviously the best solution . Thanks again EnTerr - Keep them coming. I'm hoping to hear more from you on LRU algorithms, caching, and their application in the ROKU model. Those will be upcoming subjects.

Just change the bitmap.Clear to :
bitmap.Clear ( RGBA_to_int( RND(200), RND(155), RND(50), 255) )
My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )
0 Kudos
NewManLiving
Visitor

Re: roscreen grid

One User discovered a bug in the code. What happened is that I changed the pageSize and Margin members to their corresponding grid family ie.. RowPageSize, ColPageSize, RowMargin etc.. However, I did not change all names across the board. So for now if you are using the grid do not change any of the parameters
that are sent to construct the grid. I did not test it with other row/col page sizes ( like I normally do ) since I just copied the code for the most part, But I did change
a few names in the config and grid objects which obviously do not match now. So I will get to this later. I know where the problem is but I am not able to fix it at this
time
John
My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )
0 Kudos
NewManLiving
Visitor

Re: roscreen grid

My apologies first to the moderators for including all this code. But I have to do it again . I decided to post the debug version of the grid with the testing generator.
There is just too much complexity in the grid to hastily extract it from my framework. Anyway, the mistakes were mostly related to changing the variable names, but not
across the board. Additionally, the region size was miscalculated under certain circumstances, thus the virtual column was not always hidden or was not able to disguise itself as a valid cell. I asked a friend who would like to use the grid why he would ever want to. I told him it would be my last resort as it requires a very robust caching system due to being open at both ends. As I explained in the code, I prefer to use a close-ended grid either row or column along with a list-menu. I do use the open ended grid but I generate bitmaps using skins and special fonts as I go along. They are written into the bitmap buffer and then destroyed. Of course if you are displaying poster art you cannot due this, but there are still better ways to do it other than this type of grid. The real problem is not the grid, once you use the testing generator you will see
that the grid itself is pretty solid and at max consumes very little memory. To me the real problem has been the inability to know how much memory is available. Until the recent release of r2d2_bitmaps no one knew for sure. I get the impression that it was offered with reluctance. Along with this we did not know for sure how much temp space was available for the developer until very recently. If I am wrong on this please correct me. I have not been on this board a very long time. Without knowing this information it is impossible to create any realistic caching system to meet the needs of the open-ended grids. I would hope that this information along with further telnet commands will be made available to all developers. Thankfully, Mark and others have given us something to work with
Anyway the generator maxes the memory out creating various size bitmaps. The maximum parameters that are hardcoded eg max cellwidth, height, margin. Along with
the maxsize of the bitmap generator are about all you are going to get. Increasing these values will eventually produce a testing value that will fail. You can only create so many bitmaps. The debug version here at least lets you know where the problem is. Again I have submitted this with parameters that reach their limit, but are just short
of consuming all the memory, at least after running it in a loop for awhile. The only other caveat is that this version of the grid does not handle dynamic width sizes. All rows have a fixed number of columns. Other than that it is fun to play with generating all kinds of grids. If you want a debug free version just message me privately,
I do not feel comfortable posting all this code here again




Function Main() As Void

m.Channel = NewChannel()
m.Channel.Initialize()

m.Channel.Compositor.SetDrawTo( m.Channel.Screen, m.Channel.ScrBkgClr )

VirtualGridTest()

m.Channel.Clear()
m.Channel = Invalid

End Function

' ASSERT is used only to short-circuit my own exception handler
' It is only meant for development testing and example code, NOT production
' It would be impossible to bring in my entire framework for one example
' Exception handling is your framework's job. The listview expects data
' to be in order. It does some minor checking for you but that is it
Function ASSERT(a_boolean As Boolean, l_object = "" As String ) As Void

if not a_boolean

print "=========================="
print l_object+" Failed ASSERT"
print "=========================="

stop
end if

End Function

' ************** APPLICATION OBJECT ***************************
' I use this to emulate my own environment. To make is easier for me to
' Bring in code from my own production application.
Function NewChannel() As Object

nc = CreateObject("roAssociativeArray")

' I use one roScreen swapping in/out composites to emulate stacked screens
' and one port as I employ the roSystemLog component.
nc.Screen = Invalid
nc.Compositor = Invalid

nc.Port = Invalid
nc.Device = Invalid
nc.FontRegistry = Invalid

nc.Font20 = Invalid
nc.Font28 = Invalid

nc.DisplayWidth = 0
nc.DisplayHeight = 0

' Default Colors
nc.Transparent = &h00000000
nc.ScrBkgClr = &hE0DFDFFF
nc.DlgBkgClr = &hCECECEFF
nc.ItemTxtClr = &h383838FF

' Remote (you can use bslCore for these if you like)
nc.kp_BK = 0
nc.kp_UP = 2
nc.kp_DN = 3
nc.kp_LT = 4
nc.kp_RT = 5
nc.kp_OK = 6
nc.kp_RW = 7
nc.kp_REV = 8
nc.kp_FWD = 9
nc.kp_INFO = 10

nc.MIN_CELL_WIDTH = 100
nc.MIN_CELL_HEIGHT = 100
nc.MAX_CELL_WIDTH = 250
nc.MAX_CELL_HEIGHT = 250

nc.MIN_ROW_MARGIN = 0
nc.MAX_ROW_MARGIN = 60
nc.MIN_COL_MARGIN = 0
nc.MAX_COL_MARGIN = 30
nc.AVG_COL_MARGIN = 15

nc.Initialize = channel_initialize

' can include interface to create the default
' roCompositor with your overhang at the top
return nc

End Function


Function channel_initialize() As Void

m.Port = CreateObject("roMessagePort")
m.Device = CreateObject("roDeviceInfo")
m.FontRegistry = CreateObject("roFontRegistry")

m.Font20 = m.FontRegistry.GetDefaultFont(20, False, False)
ASSERT(type(m.Font20) = "roFont", "m.Font20")

m.Font28 = m.FontRegistry.GetDefaultFont(28, False, False)
ASSERT(type(m.Font28) = "roFont", "m.Font28")

m.Compositor = CreateObject("roCompositor")
ASSERT(type(m.Compositor) = "roCompositor", "m.Compositor")

m.Screen = CreateObject("roScreen", True)
ASSERT(type( m.Screen) = "roScreen", "m.Screen")

l_wh = m.Device.GetDisplaySize()
m.DisplayWidth = l_wh.w
m.DisplayHeight = l_wh.h

m.Screen.SetPort(m.Port)
m.Screen.Clear(m.ScrBkgClr)
m.Screen.SwapBuffers()

End Function


' 224, 200
'************************* Testing Module ***********************************************
Function VirtualGridTest() As Void

max_number = 10
test_values = CreateObject("roArray", max_number, False)

Config = NewGridConfig()
VGrid = NewVGrid( m.Channel.Compositor )

l_testing = True
l_testNum = 1

while( l_testing )

CellWidth = RND(400)
CellHeight = RND(400)
RowPageSize = RND(25)
ColPageSize = RND(25)
RowMargin = RND(100)
ColMargin = RND(50)

if CellWidth < m.Channel.MIN_CELL_WIDTH then CellWidth = m.Channel.MIN_CELL_WIDTH
if CellWidth > m.Channel.MAX_CELL_WIDTH then CellWidth = m.Channel.MAX_CELL_WIDTH
if CellHeight < m.Channel.MIN_CELL_HEIGHT then CellHeight = m.Channel.MIN_CELL_HEIGHT
if CellHeight > m.Channel.MAX_CELL_HEIGHT then CellHeight = m.Channel.MAX_CELL_HEIGHT

print ""
print "TEST NUMBER "+l_testNum.ToStr()
print "====================================================================="
print "Requested CellWidth : ";CellWidth
print "Requested CellHeight : ";CellHeight
print "Requested RowPageSize : ";RowPageSize
print "Requested ColPageSize : ";ColPageSize
print "Requested RowMargin : ";RowMargin
print "Requested ColMargin : ";ColMargin

test_values.Clear()
for i = 0 to max_number - 1
test_values.Push( GetVCBitmaps( max_number, chr(65 + i), CellWidth, CellHeight) )
end for

Config.Data = test_values
Config.RowPageSize = RowPageSize
Config.ColPageSize = ColPageSize
Config.RowMargin = RowMargin
Config.ColumnMargin = ColMargin
Config.CellWidth = CellWidth
Config.CellHeight = CellHeight

if VGrid.Set( Config )

x = int( m.Channel.DisplayWidth / 2 - VGrid.GetWidth() / 2 )
y = int( m.Channel.DisplayHeight / 2 - VGrid.GetHeight() / 2 )

VGrid.MoveTo( x, y )
VGrid.Show()

l_testing = VGrid.EventLoop() <> 0

vGrid.Reset()

else

print "VGrid.Set Failed"
l_testing = False

end if

l_testNum = l_testNum + 1

end while

Config.Clear()
Config = Invalid

test_values.Clear()

VGrid.Clear()
VGrid = Invalid

return

End Function


' Simple configuration and container classes/structures which have no interator interface -
' removes levels of indirection and keeps things easier to manage and read.
' Indexing is handled by the grid members *count *index

' ****************** GRID CONFIGURATION ************************************

Function NewGridConfig() As Object

gc = CreateObject("roAssociativeArray")

' Pointer to array of arrays of bitmaps
gc.Data = Invalid

gc.BkgClr = m.Channel.ScrBkgClr
gc.TextFont = m.Channel.Font28
gc.TextClr = m.Channel.ItemTxtClr

' Desired maximum row page size (number of child grid rows visible on one page)
gc.RowPageSize = 5
' Desired maximum Column page size for the child gridrows (number of visible columns)
gc.ColPageSize = 5
' Default Grids cell width and height
gc.CellWidth = m.Channel.MIN_CELL_WIDTH
gc.CellHeight = m.Channel.MIN_CELL_HEIGHT

' Row and column margins
gc.RowMargin = m.Channel.MIN_ROW_MARGIN
gc.ColumnMargin = m.Channel.AVG_COL_MARGIN

' Start Index offset of what grid row to display at the top
gc.StartIndex = 0


gc.Reset = Function()

m.Data = Invalid
m.BkgClr = m.Channel.ScrBkgClr
m.TextFont = m.Channel.Font28
m.TextClr = m.Channel.ItemTxtClr
m.RowPageSize = 5
m.ColPageSize = 5
m.CellWidth = m.Channel.MIN_CELL_WIDTH
m.CellHeight = m.Channel.MIN_CELL_HEIGHT
m.RowMargin = m.Channel.MIN_ROW_MARGIN
m.ColumnMargin = m.Channel.AVG_COL_MARGIN

End Function

return gc

End Function


' *********************** GRID CONTAINER *************************************************************

Function NewGridContainer() As Object

gc = CreateObject("roAssociativeArray")

gc.Rows = Invalid

gc.Set = Function( a_rowCount As Integer, a_rowHeight As Integer )

m.Reset()

m.Rows = CreateObject( "roArray", a_rowCount, False )

l_x = 0
l_y = 0

for l_i = 0 to a_rowCount - 1

m.Rows.Push( NewGridRow( l_x, l_y ) )

l_y = l_y + a_rowHeight

end for

End Function


gc.Reset = Function()

if m.Rows <> Invalid

for each l_row in m.Rows : l_row.Clear() : end for

end if

m.Rows = Invalid

End Function

return gc

End Function


Function NewGridRow( a_x As Integer, a_y As Integer ) As Object

gc = CreateObject("roAssociativeArray")

gc.VGCRow = NewVGridCRow()

gc.X = a_x
gc.Y = a_y

return gc

End Function

' ********************************* DATA CONTAINER ************************************
' A container for the data sent to the grid. Since rows are virtual each child row needs
' a way to remember where it left off when it is reloaded into the virtual row
Function NewVDataContainer() As Object


dc = CreateObject("roAssociativeArray")

dc.Data = Invalid

dc.Set = Function( a_dataArray As Object )

m.Reset()

m.Data = CreateObject( "roArray", a_dataArray.Count(), False )

for each l_items in a_dataArray

m.Data.Push( NewDataObject( l_items ) )

end for

End Function


dc.Reset = Function ()

if m.Data <> Invalid then m.Data.Clear()

m.Data = Invalid

End Function


return dc

End Function


Function NewDataObject( a_items As Object ) As Object

do = CreateObject("roAssociativeArray")

do.Items = a_items
do.StartIndex = 0

return do

End Function

' *****************************************************************



' ********************************* VIRTUAL GRID OBJECT ************************************
' This is an extracted, and simplified version of one of my grids. I tend more to use
' row based grids than column based. However, since someone was interested here is the virtual beast
' That has both row and column virtual buffers. In this grid the buffers are one column size for the
' columns, and one row size for the rows. I have other grids that have multiple buffers. But this one
' can load data very fast IF..... you can supply it. At is largest size it consumes < 6 MB of memory but
' is able to handle all the bitmaps you can feed it. That is where the problem lies. To feed this type of
' beast you need to have a very robust data caching system which involves both memory and disk. This grid
' example is not tied in with any data engine as there is just too much code and complexity involved. It is an example
' of a virtual grid. My own method of development uses a more partioned or layered style, that way data is
' introduced in logical sequences. A perfect example is the listview/ row-based grid combo
' I use one screen, multiple compositors (only one is needed for each module implemented)
' But components need to know their parents screen, compositor, and the zorder to place themselves
' I also employ one port as I use the roSystemLog component. To implement this style of grid it is absolutely
' necessary to use sprites as you will see in the code. And, to use sprites, you need a compositor. Trust me
' once you get used to "Compositor, bitmaps, regions, sprites" concept, you will never go back to the old way. There is
' so much more power, speed and a ton of animation fun with the regions. You know, when you have a grid like this
' and the user has to wait a few seconds or more to load other pages, - you just give them something to look at. My grids
' load with the cell data flying, rotating in, or fading in while my custom cursors are providing a side-show
' of their own. I call this the Illusion of Speed. But you cannot do this type of complexity with just an roScreen
' and some bitmaps. You just get a sluggish mess. Bottom line, I'm putting it out as is. Many things are fixed
' as time and space does not permit and honestly, I do not think very many people are interested in the API. There
' is alot you can do. My own grids have dynamic columns that can slide into center or anywhere else and the selector knows
' which cells have data and which are still loading or do not have data (invisible cells) the selector simply glides
' over these areas. You will see all this if I ever get my own application out. If you have any questions
' you can always ask. But this is a lot of work for a few interested people. So the commenting is limited. Most
' of it is expalined in other examples

' How this works: The top row is the only row that scrolls column wise. There are two modes. The row mode
' and the column mode. To move from row to row you use the up down arrow keys and the paging keys. using the
' left right keys puts you in column mode usng the paging keys in this mode moves the columns very quickly.
' Resuming with up/down controls puts you back in row mode. Flat paging of data has been left out for size
' Regions. This is the third mode which is not included in this example. If you want it just ask

' Most of this code is explained in depth in the wisdom and roscreen grid threads. Where it is not I will explain
Function NewVGrid( a_compositor As Object, a_zOrder = 1 As Integer ) As Object

vg = CreateObject("roAssociativeArray")

' Used only to test the code
vg.LastKey = -1

vg.Channel = m.Channel
vg.Screen = m.Channel.Screen
vg.Port = m.Channel.Port
vg.Compositor = a_compositor

' The Zorder to place the listview
vg.ZOrder = a_zOrder

' Default Colors, fonts
vg.BkgClr = m.Channel.ScrBkgClr
vg.TextFont = m.Channel.Font28
vg.TextClr = m.Channel.ItemTxtClr

' A child row configuatoin structure which is
' used for the virtual row
vg.Config = Invalid

' As stated above I keep the structures simple as I am not sure
' how optimized indirection is in BrightScript. I only read the docs when I have to.
' The GridContainer
vg.GC = Invalid
vg.Rows = Invalid
vg.RowCount = 0
vg.RowIndex = 0

' The Data Container
vg.DC = Invalid
vg.Data = Invalid
vg.DataCount = 0
vg.DataIndex = 0

' What row to start with
vg.StartIndex = 0

' Number of grid rows to display
vg.RowPageSize = 0
' Number of grid columns to display
vg.ColPageSize = 0

' RowHeight * RowPageSize
vg.PageHeight = 0
vg.PageNumber = 0
vg.TotalPages = 0

vg.IsPartialPage = False
vg.IsPagingDown = True

' Cell Height + Row Margin
vg.RowHeight = 0
vg.RowMargin = 0

' Cell Width + Column Margin
vg.ColumnWidth = 0
vg.ColumnMargin = 0
vg.CellWidth = 0
vg.CellHeight = 0

' The bitmap for the child rows to draw into
'
vg.BufferBitmap = Invalid
vg.BufferRegion = Invalid
vg.BufferSprite = Invalid

' Always dynamically calculated
vg.BufferWidth = 0
vg.BufferHeight = 0
vg.RegionWidth = 0
vg.RegionHeight = 0

' PUBLIC
vg.Set = virtual_grid_set
vg.Show = virtual_grid_show
vg.Hide = virtual_grid_hide
vg.MoveTo = virtual_grid_moveto
vg.GetWidth = virtual_grid_getwidth
vg.GetHeight = virtual_grid_getheight
vg.ScrollRow = virtual_grid_scrollrow

' Used in debug only
vg.GetLastKey = Function() As Integer
return m.LastKey
End Function

' The event loop can be used or you can control the grid yourself
' with the above functions
vg.EventLoop = virtual_grid_eventloop

' PROTECTED
vg.DrawPage = virtual_grid_drawpage
vg.DrawAll = virtual_grid_drawall
vg.Reset = virtual_grid_reset

return vg

End Function

' Releases any sprite memory and resets all members to default
Function virtual_grid_reset() As Void

if m.BufferSprite <> Invalid then m.BufferSprite.Remove()
if m.GC <> Invalid then m.GC.Reset()
if m.DC <> Invalid then m.DC.Reset()

m.LastKey = -1

m.GC = Invalid
m.Rows = Invalid
m.RowCount = 0
m.RowIndex = 0

m.DC = Invalid
m.Data = Invalid
m.DataCount = 0
m.DataIndex = 0

m.TextFont = m.Channel.Font28
m.TextClr = m.Channel.ItemTxtClr
m.BkgClr = m.Channel.ScrBkgClr

m.Config = Invalid

m.StartIndex = 0

m.RowPageSize = 0
m.ColPageSize = 0

m.PageHeight = 0
m.PageNumber = 0
m.TotalPages = 0
m.IsPagingDown = True
m.IsPartialPage = False

m.RowHeight = 0
m.RowMargin = 0

m.ColumnWidth = 0
m.ColumnMargin = 0
m.CellWidth = 0
m.CellHeight = 0

m.BufferBitmap = Invalid
m.BufferRegion = Invalid
m.BufferSprite = Invalid
m.BufferWidth = 0
m.BufferHeight = 0
m.RegionWidth = 0
m.RegionHeight = 0

return

End Function

' Sets up the grid
Function virtual_grid_Set( a_config As Object ) As Boolean

' Release resources and re-initialize
m.Reset()

' Assign the configuration data
m.RowPageSize = a_config.RowPageSize
m.ColPageSize = a_config.ColPageSize
m.StartIndex = a_config.StartIndex
m.CellWidth = a_config.CellWidth
m.CellHeight = a_config.CellHeight
m.RowMargin = a_config.RowMargin
m.ColumnMargin = a_config.ColumnMargin
m.BkgClr = a_config.BkgClr
m.TextFont = a_config.TextFont
m.TextClr = a_config.ItemTxtClr

' Normalize the data/count use your own error system. I do not even have returns. I'm from the old school
' Create a debug version using exception handlng, throws, asserts and a production version free of the nonsense
' I just make sure what I am 'setting' is accurate
l_dataCount = a_config.Data.Count()
ASSERT( l_dataCount > 0, "virtual_grid_Set: l_dataCount = "+l_dataCount.ToStr() )
if l_dataCount = 0 then return False

' In this examle cannot have row/col page sizes as follows:
if m.RowPageSize >= l_dataCount then m.RowPageSize = l_dataCount - 1
if m.RowPageSize <= 0 then m.RowPageSize = 1

' Cannot have a page size the same as the data size except 1
if m.ColPageSize >= l_dataCount then m.ColPageSize = l_dataCount - 1
if m.ColPageSize <= 0 then m.ColPageSize = 1

' Total number of pages if the code above is correct then
' This type of paging is not shown in this example
m.TotalPages = int( l_dataCount / m.RowPageSize )
m.IsPartialPage = ( l_dataCount MOD m.RowPageSize ) > 0
if m.IsPartialPage then m.TotalPages = m.TotalPages + 1

' Sizes are fixed for the examle, but generous. And I have not tested all combos
' You get it as is. My own production code is far more complex so I have
' to pick and pull out things and don't have time to test everytning. But if you find something
' or need something I will fix or provide
if m.CellWidth < m.Channel.MIN_CELL_WIDTH then m.CellWidth = m.Channel.MIN_CELL_WIDTH
if m.CellWidth > m.Channel.MAX_CELL_WIDTH then m.CellWidth = m.Channel.MAX_CELL_WIDTH

if m.CellHeight < m.Channel.MIN_CELL_HEIGHT then m.CellHeight = m.Channel.MIN_CELL_HEIGHT
if m.CellHeight > m.Channel.MAX_CELL_HEIGHT then m.CellHeight = m.Channel.MAX_CELL_HEIGHT

if m.RowMargin < m.Channel.MIN_ROW_MARGIN then m.RowMargin = m.Channel.MIN_ROW_MARGIN
if m.RowMargin > m.Channel.MAX_ROW_MARGIN then m.RowMargin = m.Channel.MAX_ROW_MARGIN

if m.ColumnMargin < m.Channel.MIN_COL_MARGIN then m.ColumnMargin = m.Channel.MIN_COL_MARGIN
if m.ColumnMargin > m.Channel.MAX_COL_MARGIN then m.ColumnMargin = m.Channel.MAX_COL_MARGIN

' Calculate total buffer width - Adjust for out-of-bounds
m.ColumnWidth = m.CellWidth + m.ColumnMargin
l_maxWidth = m.Channel.DisplayWidth
l_buffWidth = m.ColPageSize * m.ColumnWidth
if l_buffWidth > l_maxWidth then m.ColPageSize = int( l_maxWidth / m.ColumnWidth )

' Total buffer width including the virtual cell (col page size is actually the child row grid size)
m.BufferWidth = m.ColPageSize * m.ColumnWidth + m.ColumnWidth

' Calculate total buffer height
m.RowHeight = m.CellHeight + m.RowMargin
l_maxHeight = m.Channel.DisplayHeight
l_buffHeight = m.RowPageSize * m.RowHeight
if l_buffHeight > l_maxHeight then m.RowPageSize = int( l_maxHeight / m.RowHeight )

' The buffer height - and a row height for the virtual buffer
m.BufferHeight = m.RowPageSize * m.RowHeight + m.RowHeight

ASSERT( m.BufferHeight > 0 and m.BufferHeight < 2000, "virtual_grid_Set: m.BufferHeight = "+m.BufferHeight.ToStr() )
ASSERT( m.BufferWidth > 0 and m.BufferWidth < 2000, "virtual_grid_Set: m.BufferWidth = "+m.BufferWidth.ToStr() )
' Finally, create the bitmap buffer
print ""
print "AFTER CALL TO VGRID.SET Requests Adjusted To: And Final BitmapBuffer Width and Height:"
print "VGRID.CellWidth : ";m.CellWidth
print "VGRID.CellHeight : ";m.CellHeight
print "VGRID.RowPageSize : ";m.RowPageSize
print "VGRID.ColPageSize : ";m.ColPageSize
print "VGRID.RowMargin : ";m.RowMargin
print "VGRID.ColumnMargin : ";m.ColumnMargin
print "VGRID.BufferWidth : ";m.BufferWidth
print "VGRID.BufferHeight : ";m.BufferHeight

m.BufferBitmap = CreateObject( "roBitmap", {width: m.BufferWidth, height: m.BufferHeight , AlphaEnable: False} )
ASSERT( m.BufferBitmap <> Invalid, "virtual_grid_Set: m.BufferBitmap = Invalid" )
if m.BufferBitmap = Invalid then return False
m.BufferBitmap.Clear( m.BkgClr )

' Page width , region width (cut off the the hidden column ( and the end margin for even scrolling ))
m.PageHeight = m.RowPageSize * m.RowHeight
m.RegionWidth = m.BufferWidth - m.ColumnWidth - m.ColumnMargin
m.RegionHeight = m.PageHeight - m.RowMargin

m.BufferRegion = CreateObject("roRegion", m.BufferBitmap, 0, 0, m.RegionWidth, m.RegionHeight)
ASSERT( m.BufferRegion <> Invalid, "virtual_grid_Set: m.BufferRegion = Invalid" )
if m.BufferRegion = Invalid then return False
' Set the regions wrap to true for scrolling. This must always be done
m.BufferRegion.SetWrap(True)

' The Interface members show, moveto, will handle this later
m.BufferSprite = m.Compositor.NewSprite(0, 0, m.BufferRegion, -1)
ASSERT( m.BufferSprite <> Invalid, "virtual_grid_Set: m.BufferSprite = Invalid" )
if m.BufferSprite = Invalid then return False

' Create the grid definition container m.RowPageSize + 1 for virtual row
' Initialize a pointer to the rows member for easier access
' Set the count and start index. I use the grid to do this for speed. I am not sure how
' optimal indirection is as there is no true oo in brightscript - the lmited m pointer is a clear example of this
m.GC = NewGridContainer()
m.GC.Set( m.RowPageSize + 1, m.RowHeight )

m.Rows = m.GC.Rows
m.RowCount = m.Rows.Count()
m.RowIndex = 0

' Set the containers data member and hold on to another
' reference in the grids data member for easier access
' Get the total count and set the start index

m.DC = NewVDataContainer()
m.DC.Set( a_config.Data )

m.Data = m.DC.Data
m.DataCount = m.Data.Count()

' Create the grid cell configuration object and call set on each
' of the child rows except the virtual row which will contain
' a child row with no data set at this time

m.Config = NewGridConfig()

' Each child will draw onto the parents bitmap
' As instructed by the parent grids settings
'm.Config.ColPageSize = m.ColPageSize
m.Config.Compositor = m.Compositor
m.Config.BufferBitmap = m.BufferBitmap
m.Config.CellWidth = m.CellWidth
m.Config.CellHeight = m.CellHeight
m.Config.ColPageSize = m.ColPageSize
m.Config.ColumnMargin = m.ColumnMargin
m.Config.BkgClr = m.BkgClr
m.Config.TextFont = m.TextFont
m.Config.TextClr = m.ItemTxtClr
m.Config.X = 0
m.Config.Y = 0


' Set each childs data and x, y pos in the grid
for l_i = 0 to m.RowPageSize - 1

m.Config.Data = m.Data[l_i]

l_vgcrow = m.Rows[ l_i ].VGCRow
l_vgcrow.Set( m.Config )

m.Config.Y = m.Config.Y + m.RowHeight

end for

m.DataIndex = l_i - 1

m.DrawPage()

return True

End Function


' Instructs the compositor to draw all sprites and then the screen swaps out the back buffer
' Dont need to call Flush it is called for you in SwapBuffers
Function virtual_grid_drawall() As Void

m.Compositor.DrawAll()
m.Screen.SwapBuffers()

End Function

' Move the sprite to where you like. The drawall arg allows you to stack drawing before dumping
' And Yes it will fail if objects are invalid. Use the assert or a debug version
Function virtual_grid_Moveto(a_x = 0 As Integer, a_y = 0 As Integer, a_isDrawAll = False As Boolean ) As Void

m.BufferSprite.MoveTo(a_x, a_y)

if a_isDrawAll then m.DrawAll()

End Function

' Show it
Function virtual_grid_show(a_isDrawAll = True As Boolean) As Void

m.BufferSprite.SetZ(m.ZOrder)

if a_isDrawAll then m.DrawAll()

End Function


Function virtual_grid_hide(a_isDrawAll = True As Boolean) As Void

m.BufferSprite.SetZ(-1)

if a_isDrawAll then m.DrawAll()

End Function

Function virtual_grid_getwidth() As Integer

return m.BufferRegion.GetWidth()

End Function


Function virtual_grid_getheight() As Integer

return m.BufferRegion.GetHeight()

End Function


' Draws the complete page starting at y pos 0 in the bitmap buffer. DataIndex is calculated in the
' virtual_grid_scrollpage handler. Each child row is told to draw itself within the parent bitmap buffer
' This member does not wirte to the the virtual row, but it always resets the virtual index pointer
' To the last row in the buffer. When a page is drawn paging down is always true
Function virtual_grid_drawpage() As Void

for l_i = 0 to m.RowPageSize - 2

m.Rows[l_i].VGCRow.DrawPage()

end for

m.Rows[l_i].VGCRow.DrawPage()

m.RowIndex = l_i
m.IsPagingDown = True

End Function

' When in row mode this function setsup the indexes, writes to the virtual row and then scrolls it into
' the viewable region. Actually is offsets the region which is the reason for most of the complexity
Function virtual_grid_scrollrow( a_isDown = True As Boolean, a_frames = 16 As Integer ) As Void

' If user transitions from scrolling up/down you must adjust the dataindex and virtualindex
' by a page size since the indexes are either at the top or bottom of the visible region
' depending on the direction the user is moving. You can then resume row scrolling in the
' new direction

' In this case, the user wants to move downward
if a_isDown

' Transition adjustment made proceed with row-based indexing
if m.IsPagingDown

m.DataIndex = m.DataIndex + 1
if m.DataIndex >= m.DataCount then m.DataIndex = 0

m.RowIndex = m.RowIndex + 1
if m.RowIndex >= m.RowCount then m.RowIndex = 0

else

' User just changed directions
' So both indexes have to be bumped up a page. There are other ways to do this
' in my past experience I have seen indexes which keep track of the top and bottom rows
' I just offset and correct

' Data Index
m.DataIndex = m.DataIndex + m.RowPageSize
if m.DataIndex >= m.DataCount then m.DataIndex = m.DataIndex - m.DataCount

m.RowIndex = m.RowIndex + m.RowPageSize
if m.RowIndex >= m.RowCount then m.RowIndex = m.RowIndex - m.RowCount

' Adjustment made, set the flag to true
m.IsPagingDown = True

end if

else
' The user is now scrolling down and the transition has been made proceed row-based indexing
if not m.IsPagingDown

m.DataIndex = m.DataIndex - 1
if m.DataIndex < 0 then m.DataIndex = m.DataCount - 1

m.RowIndex = m.RowIndex - 1
if m.RowIndex < 0 then m.RowIndex = m.RowCount - 1

else
' Need to transition and turn the flag off - note that you are adding a negative number here
' if < 0
m.DataIndex = m.DataIndex - m.RowPageSize
if m.DataIndex < 0 then m.DataIndex = m.DataCount + m.DataIndex

m.RowIndex = m.RowIndex - m.RowPageSize
if m.RowIndex < 0 then m.RowIndex = m.RowCount + m.RowIndex

m.IsPagingDown = False

end if

end if

' Since the virtual row is not fixed as the region moves but the bitmap is stationary
' and the region wraps around you are always keeping ahead of the region like a dog chasing
' its tail. Get the row below or above the region (depending on which direction)
' the get the data at the data containers index as also set above. Then set that childs
' data to reflect the new index. Then call the childs draw page to write to the bitmap
l_row = m.Rows[ m.RowIndex ]

m.Config.Data = m.Data[ m.DataIndex ]
m.Config.X = l_row.X
m.Config.Y = l_row.Y

l_row.VGCRow.Set( m.Config )
l_row.VGCRow.DrawPage()

' Now just Scroll the row into view
l_offset = 0
l_prevset = 0
l_offdiff = 0

if a_frames > 200 then a_frames = 200
if a_frames < 2 then a_frames = 2

for l_i = 1 to a_frames

l_offset = int(m.RowHeight * l_i / a_frames)
l_offdiff = l_offset - l_prevset

if l_offdiff > 0

if not a_isDown l_offdiff = -l_offdiff

m.BufferRegion.Offset(0, l_offdiff, 0, 0)

m.DrawAll()

l_prevset = l_offset

end if

end for

return

End Function


' This example uses the provided event loops but you can just use the interface
' You can expand upon the configuration object to include speeds and other user initiated handling
Function virtual_grid_eventloop() As Integer

l_index = 0
l_lastKey = -1
l_running = True

l_normalSpeed = 180
l_scrollSpeed = l_normalSpeed

l_spriteX = m.BufferSprite.GetX()
l_spriteY = m.BufferSprite.GetY()

l_kp_BK = m.Channel.kp_BK
l_kp_UP = m.Channel.kp_UP
l_kp_DN = m.Channel.kp_DN
l_kp_LT = m.Channel.kp_LT
l_kp_RT = m.Channel.kp_RT
l_kp_REV = m.Channel.kp_REV
l_kp_FWD = m.Channel.kp_FWD
l_kp_OK = m.Channel.kp_OK
' Used for testing
l_kp_RW = m.Channel.kp_RW

while( l_running )

l_msg = wait( l_scrollSpeed, m.Port )

if type(l_msg) = "roUniversalControlEvent"

l_index = l_msg.GetInt()
l_lastKey = l_index

if l_index = l_kp_RT or l_index = l_kp_LT

' Determine the index of the top row by diving the regions current y pos
' by the row height. Recall that in building the grid these are all multiples
' of each other. I am working on a grid that has various size rows.
m.TopIndex = m.BufferRegion.GetY() / m.RowHeight
' Now get the child from the grid container and call its show member
' the return value is the lastkey pressed by the user which to get out of
' column mode would be up or down. The sprite position is not changed here so
' you can just save them as I did above . More on this when you get to the
' show method
l_vgcRow = m.Rows[ m.TopIndex ].VGCRow
l_lastKey = l_vgcRow.Show( l_spriteX, l_spriteY, l_index, m.ZOrder + 1 )

' Determine the lastkey in colum mode if an exit then exit other wise go in
' the user supplied direction
if l_lastKey = l_kp_BK or l_lastKey = l_kp_RW

l_running = False
l_index = l_lastKey

else

m.ScrollRow( l_lastKey = l_kp_DN )

end if

else if l_index = l_kp_UP or l_index = l_kp_DN

m.ScrollRow( l_index = l_kp_DN )

else if l_index = l_kp_FWD or l_index = l_kp_REV

m.ScrollRow( l_index = l_kp_FWD )

else if l_index > 100 ' All key ups are value + 100

l_lastKey = -1
l_scrollSpeed = l_normalSpeed

else if l_index = l_kp_BK

l_running = False

' USED FOR DEBUG ONLY MUST BE COMMENTED OUT
else if l_index = l_kp_RW

l_running = False

else if l_index = l_kp_OK

end if

else

if l_lastKey = l_kp_UP or l_lastKey = l_kp_DN

l_scrollSpeed = 5
m.ScrollRow(l_lastKey = l_kp_DN)

else if l_lastKey = l_kp_FWD or l_lastKey = l_kp_REV

l_scrollSpeed = 5
m.ScrollRow(l_lastKey = l_kp_FWD, 0)

end if

end if

end while

m.LastKey = l_index

return l_index

End Function


'********************************** GRID ROW OBJECT AND CONTAINER *************************************
' This is just a stripped down version of the grid row example on the roscreen grid thread
' It is a child of the grid above and is managed by the grid. When in column mode it functions
' independantly
Function NewGridRowContainer() As Object

gc = CreateObject("roAssociativeArray")

gc.Rows = Invalid

gc.Set = Function( a_rows As Integer, a_cols As Integer ) As Void

if m.Rows <> Invalid then m.Reset()

m.Rows = CreateObject("roArray", a_rows, False)

for l_i = 0 to a_rows - 1

l_cols = CreateObject("roArray", a_cols, False)

for l_j = 0 to a_cols - 1

l_cols.Push( NewGridCell() )

end for

m.Rows.Push( l_cols )

end for

End Function


gc.Reset = Function() As Void

if m.Rows <> Invalid

for each l_col in m.Rows : l_col.Clear() : end for

end if

m.Rows.Clear()
m.Rows = Invalid

End Function

return gc

End Function


Function NewGridCell(a_x = 0 As Integer, a_y = 0 As Integer) As Object

gc = CreateObject("roAssociativeArray")

gc.X = a_x
gc.Y = a_x

return gc

End Function



' ************************** GRID ROW OBJECT ***********************************************
Function NewVGridCRow() As Object

gr = CreateObject("roAssociativeArray")

' Used for debug versions
gr.LastKey = -1

' System application object, screen port
gr.Channel = m.Channel
gr.Screen = m.Channel.Screen
gr.Port = m.Channel.Port

' Ref to implementor's compositor
gr.Compositor = Invalid

' Description of rows, cells and a ref to the one row in the container
gr.GridContainer = Invalid
gr.GridColums = Invalid
gr.GridColCount = 0
gr.GridColIndex = 0

' Holds a reference to your data container.
gr.Data = Invalid
gr.Items = Invalid
gr.ItemCount = 0
gr.ItemIndex = 0

' The desired number of visible columns
gr.ColPageSize = 0
gr.ColumnMargin = 0
gr.ColumnWidth = 0 ' cell width + margin

' Cell Width/height
gr.CellWidth = 0
gr.CellHeight = 0
' Flag to alert me that the user has changed scrolling directions
gr.IsPagingDown = True

' The parent bitmap that will buffer the data.
gr.BufferBitmap = Invalid
' Reference to the dynamic region and sprite
' That scrolls the list when in focus
gr.BufferRegion = Invalid
gr.BufferSprite = Invalid
' Precalulate for speed
gr.RegionWidth = 0
gr.RegionHeight = 0
gr.X = 0
gr.Y = 0

' Default Colors
gr.BkgClr = m.Channel.ScrBkgClr
gr.TextFont = m.Channel.TextFont
gr.TextClr = m.Channel.ItemTxtClr

' PUBLIC
gr.Set = virtual_gridc_row_set
gr.Show = virtual_gridc_row_show
gr.ScrollCell = virtual_gridc_row_scrollcell
gr.EventLoop = virtual_gridc_row_eventloop

' Used in debug only
gr.GetLastKey = Function() As Integer
return m.LastKey
End Function
' PROTECTED
gr.DrawPage = virtual_gridc_row_drawpage
gr.DrawAll = virtual_gridc_row_drawall
gr.Reset = virtual_gridc_row_reset

return gr

End Function


' Releases any sprite memory and resets all members to default
Function virtual_gridc_row_reset() As Void

m.LastKey = -1

if m.BufferSprite <> Invalid then m.BufferSprite.Remove()
if m.GridContainer <> Invalid then m.GridContainer.Clear()

m.GridContainer = Invalid
m.BufferSprite = Invalid

m.GridColIndex = 0
m.ItemIndex = 0
m.StartIndex = 0

return

End Function


Function virtual_gridc_row_set( a_config As Object ) As Boolean

' Release resources and re-initialize
m.Reset()

m.CellWidth = a_config.CellWidth
m.CellHeight = a_config.CellHeight
m.ColumnMargin = a_config.ColumnMargin
m.ColPageSize = a_config.ColPageSize
m.BkgClr = a_config.BkgClr
m.TextFont = a_config.TextFont
m.TextClr = a_config.ItemTxtClr
m.BufferBitmap = a_config.BufferBitmap
m.Compositor = a_config.Compositor
m.X = a_config.X
m.Y = a_config.Y

' Get a reference to the data object
m.Data = a_config.Data

' Resolve the items member
m.Items = m.Data.Items
m.ItemCount = m.Items.Count()
m.ItemIndex = m.Data.StartIndex

' Cannot have a page size the same as the data size except 1
if m.ColPageSize >= m.ItemCount then m.ColPageSize = m.ItemCount - 1
if m.ColPageSize <= 0 then m.ColPageSize = 1

' Calculate total column width
m.ColumnWidth = m.CellWidth + m.ColumnMargin

' The region width - this should never exceed the parents m.RegionWidth, but it can be smaller, however NOT
' in this example. You need help on this one let me know
m.RegionWidth = m.ColPageSize * m.ColumnWidth - m.ColumnMargin
m.RegionHeight = m.CellHeight

' Create the grid definition container at one row, m.ColPageSize + 1 Columns
' Get reference to the members and setup indexing
m.GridContainer = NewGridRowContainer()
' Set up one extra for the virtual column
m.GridContainer.Set( 1, m.ColPageSize + 1 )
m.GridColumns = m.GridContainer.Rows[0]
m.GridColCount = m.GridColumns.Count()

' Grid cell x, y positions. Remember that they can change for the virtual row
' set is continually called for any one of the datum that needs to be reloaded but
' ususally at a different location in the bitmap buffer
l_x = m.X
l_y = m.Y

' Here you would set up the grid columns includ
for each l_col in m.GridColumns

l_col.x = l_x
l_col.y = l_y

l_x = l_x + m.ColumnWidth

end for

return True

End Function


Function virtual_gridc_row_drawall() As Void

m.Compositor.DrawAll()
m.Screen.SwapBuffers()

End Function

' Now this is how all this comes together
Function virtual_gridc_row_show( a_sx As Integer, a_sy As Integer, a_index As Integer, a_zorder As Integer ) As Integer

' the child does not create its own sprite or regoin at the time of its creation. It just simply draws itself
' into the parents bitmap until its service are required. This way the parent can simply use its own region to
' scroll the individual child rows when in fact they are just a picture of the current state of the row

' Now we have to kind of lift it off or sprite it above its own reflection.
' You must release any memory allocated for a previous sprite or it will collect
if m.BufferSprite <> Invalid then m.BufferSprite.Remove()

' Now here, recall that the m.X, m.Y members were set in the set function. so we know the x and y
' pos within the bitmap and we also know the width and height that is occupied within the parent bitmap
' All we want to do is create a seperate region and sprite it. What this does in effect is peel it off
' of the bitmap and anitmate it. SetWrap MUST BE TRUE
m.BufferRegion = CreateObject( "roRegion", m.BufferBitmap, m.X, m.Y, m.RegionWidth, m.RegionHeight )
m.BufferRegion.SetWrap( True )
' Now just sprite it right above its reflection
m.BufferSprite = m.Compositor.NewSprite( a_sx, a_sy, m.BufferRegion, a_zorder )

' Call the scroll member with the direction indicated by the user (resolves to true or false)
m.ScrollCell( a_index = m.Channel.kp_RT )

' Then go into the event loop with the current key index so that last_key can be setup properly
' Upon return we just hand back the last key to the parent
l_index = m.EventLoop( a_index )

' Now since the sprite is only an illusion riding on top of the contents of the parent
' we need to repaint the parent to reflect the new cell position. Recall than when the user
' moves to the right, the indexes are page flipped so what is seen at the top is not the
' actual index. Howeve, if the user is scrolling left then the indexing is at the top going
' backwards. So if m.IsPagingDown, you have to reset the data index to the first cell displayed
' And then redraw the page
if m.IsPagingDown

m.ItemIndex = m.ItemIndex - m.ColPageSize + 1
if m.ItemIndex < 0 then m.ItemIndex = m.ItemCount + m.ItemIndex

end if

' Set the start index to the new index so we know where to start the next time the
' row is reset
m.Data.StartIndex = m.ItemIndex

' Now just draw the page at the same column as indicated in the sprite
m.DrawPage()

' Now just remove the sprite, and draw
m.BufferSprite.Remove()
m.BufferSprite = Invalid
m.BufferRegion = Invalid

m.DrawAll()

return l_index

End Function


Function virtual_gridc_row_drawpage() As Void

for l_i = 0 to m.ColPageSize - 2

l_col = m.GridColumns[ l_i ]

m.BufferBitmap.DrawObject( l_col.X, l_col.Y, m.Items[ m.ItemIndex ] )

' Wrap around if needed
m.ItemIndex = m.ItemIndex + 1
if m.ItemIndex >= m.ItemCount then m.ItemIndex = 0

end for

' Draw the final row and set the virtual index, indexes are always at the bottom
' when drawing a page so you just set the flag to true, of course you have to
' visualize the page in column format going right x, while y is fixed at 0
l_col = m.GridColumns[l_i]
m.BufferBitmap.DrawObject(l_col.X, l_col.Y, m.Items[m.ItemIndex])

m.GridColIndex = l_i
m.IsPagingDown = True

End Function


Function virtual_gridc_row_scrollcell(a_isRight = True As Boolean, a_frames = 16 As Integer) As Void

if a_isRight

if m.IsPagingDown

m.ItemIndex = m.ItemIndex + 1
if m.ItemIndex >= m.ItemCount then m.ItemIndex = 0

m.GridColIndex = m.GridColIndex + 1
if m.GridColIndex >= m.GridColCount then m.GridColIndex = 0

else

m.ItemIndex = m.ItemIndex + m.ColPageSize
if m.ItemIndex >= m.ItemCount then m.ItemIndex = m.ItemIndex - m.ItemCount

m.GridColIndex = m.GridColIndex + m.ColPageSize
if m.GridColIndex >= m.GridColCount then m.GridColIndex = m.GridColIndex - m.GridColCount

m.IsPagingDown = True

end if

else

if not m.IsPagingDown

m.ItemIndex = m.ItemIndex - 1
if m.ItemIndex < 0 then m.ItemIndex = m.ItemCount - 1

m.GridColIndex = m.GridColIndex - 1
if m.GridColIndex < 0 then m.GridColIndex = m.GridColCount - 1

else

' Otherwise the user was formally paging right so we have to flip and set the flag
m.ItemIndex = m.ItemIndex - m.ColPageSize
if m.ItemIndex < 0 then m.ItemIndex = m.ItemCount + m.ItemIndex

m.GridColIndex = m.GridColIndex - m.ColPageSize
if m.GridColIndex < 0 then m.GridColIndex = m.GridColCount + m.GridColIndex

m.IsPagingDown = False

end if

end if

l_col = m.GridColumns[ m.GridColIndex ]
m.BufferBitmap.DrawObject( l_col.X, l_col.Y, m.Items[ m.ItemIndex ] )

l_offset = 0
l_offdiff = 0
l_prevset = 0

if a_frames < 2 then a_frames = 2
if a_frames > 200 then a_frames = 200

for l_i = 1 to a_frames

l_offset = int( m.ColumnWidth * l_i / a_frames )
l_offdiff = l_offset - l_prevset

if l_offdiff > 0

if not a_isRight then l_offdiff = -l_offdiff

m.BufferRegion.Offset( l_offdiff, 0, 0, 0 )

m.DrawAll()

l_prevset = l_offset

end if

end for

End Function


Function virtual_gridc_row_eventloop(a_lastKey = -1 As Integer, a_scrollSpeed = 180 As Integer) As Integer

l_lastKey = a_lastKey
l_scrollSpeed = a_scrollSpeed

l_index = 0
l_running = True
l_normalSpeed = 180

l_kp_BK = m.Channel.kp_BK
l_kp_UP = m.Channel.kp_UP
l_kp_DN = m.Channel.kp_DN
l_kp_LT = m.Channel.kp_LT
l_kp_RT = m.Channel.kp_RT
l_kp_REV = m.Channel.kp_REV
l_kp_FWD = m.Channel.kp_FWD
l_kp_OK = m.Channel.kp_OK
l_kp_RW = m.Channel.kp_RW

while(l_running)

l_msg = wait( l_scrollSpeed, m.Port )

if type(l_msg) = "roUniversalControlEvent"

l_index = l_msg.GetInt()
l_lastKey = l_index

if l_index = l_kp_RT or l_index = l_kp_LT

m.ScrollCell( l_index = l_kp_RT )
l_scrollSpeed = l_normalSpeed

else if l_index = l_kp_FWD or l_index = l_kp_REV

m.ScrollCell( l_lastKey = l_kp_FWD )

else if l_index > 100 ' All key ups are value + 100

l_lastKey = -1
l_scrollSpeed = l_normalSpeed

else if l_index = l_kp_BK or l_index = l_kp_DN or l_index = l_kp_UP

l_running = False
' Used for debug only MUST comment out
else if l_index = l_kp_RW

l_running = False

end if

else

if l_lastKey = l_kp_RT or l_lastKey = l_kp_LT

l_scrollSpeed = 5
m.ScrollCell(l_lastKey = l_kp_RT)

else if l_lastKey = l_kp_FWD or l_lastKey = l_kp_REV

l_scrollSpeed = 5
m.ScrollCell(l_lastKey = l_kp_FWD, 0)

end if

end if


end while

m.LastKey = l_index

return l_index

End Function

' Your on your own here. Just some test bitmap, too many will sink you
' ***************** Dummy Data ******************************
Function GetVCBitmaps(number As Integer, row As String, width As Integer, height As Integer) As Object

fontRegistry = CreateObject("roFontRegistry")
font32 = fontRegistry.GetDefaultFont(32, True, False)
bitmaps = CreateObject("roArray", number, False)

textHeight = font32.GetOneLineHeight()
y = int(height / 2 - textHeight / 2)

for i = 1 to number

bitmap = CreateObject("roBitmap", {width: width, height: height, AlphaEnable: false} )
ASSERT( bitmap <> Invalid, "GetVCBitmaps: bitmap = Invalid" )
bitmap.Clear ( RGBA_to_int( RND(200), RND(155), RND(50), 255) )

i_str = row+" "+i.ToStr()
str_w = font32.GetOneLineWidth(i_str, width)
x = int(width / 2 - str_w / 2)
bitmap.DrawText(i_str, x, y, &hFFFFFFFF, font32)

bitmaps.Push(bitmap)

end for

return bitmaps

End Function

' They are crude but they work for what they are intended for - RGB values
' EnTerr if you are reading your expertise would be greatly appreciated in any
' optimization your brain would come up with. I really like the HexToInteger3
' Although I have a different view on error checking. I put that in debug versions
' But the functions below were put together hastily and most of it came from a few
' trips around google land, actuall some are combinations of solutions other people had
Function RGBtoHexString( a_r As Integer, a_g As Integer, a_b As Integer ) As String

l_rgb = LeftShift( a_r and &hFF, 16 ) or LeftShift( a_g and &hFF, 8 ) or a_b and &hFF

return IntegerToHexString ( l_rgb )

End Function


Function IntegerToHexString( a_number As Integer ) As String

l_hexDigits = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
l_hexString = ""

for l_i = 5 to 0 step -1

l_hexString = l_hexString + l_hexDigits[ RightShift( a_number, l_i * 4 ) and &hF ]

end for

return l_hexString+"FF"

End Function


Function RightShift( a_number As Integer, a_shift As Integer ) As Integer

if a_number > 0 then a_number = Int( a_number / ( 2 ^ a_shift ) )

return a_number

End Function


Function LeftShift( a_number As Integer, a_shift As Integer ) As Integer

if a_number > 0

for l_i = 1 to a_shift

l_number = a_number and &h40000000
a_number = ( a_number and &h3FFFFFFF ) * 2
if l_number then a_number = a_number or &h80000000

end for

end if

return a_number

End Function

' Thanks enTerr
Function HexToInteger( a_hex As String ) As Integer

l_bArr = CreateObject("roByteArray")
l_bArr.FromHexString( a_hex )

l_int = 0
for each l_byte in l_bArr : l_int = 256 * l_int + l_byte : end for

return l_int

End Function


Function RGBA_to_hex(R, G, B, A) As String
b = createObject("roByteArray")
b[0] = R 'if >255, assignment trims it (as if "and &hFF")
b[1] = G
b[2] = B
b[3] = A
return b.toHexString()
End function

Function RGBA_to_int(R, G, B, A) As Integer

return &h1000000*R + &h10000*G + &h100*B + A

End Function












My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )
0 Kudos
NewManLiving
Visitor

Re: roscreen grid

One important thing I forgot to mention. To use the generator use the rewind key (circle-arrow)
My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )
0 Kudos
EnTerr
Roku Guru

Re: roscreen grid

"NewManLiving" wrote:
So readers even if you are not interested in the grid you should pick up EnTerr's RGBA_* functions and HexToInteger function and put them in your toolbox.

In your version of hexToInteger() i notice you have removed a few lines from the original, essentially
    if len(hex_in) mod 2 > 0 then hex_in = "0" + hex_in
I want to caution that not adding that extra 0 in front of a string with odd length will lead to calls like hexToInteger("7") and hexToInteger("100") to return 0, instead of the expected 7 and 256. This is because of how roByteArray.fromHexString(s) behaves with argument which length is not divisible by 2 - something i joked is a "malicious silent failure". Whether you agree with my acerbic humor is not important but make sure your function works with hex strings of both odd and even lengths
0 Kudos
NewManLiving
Visitor

Re: roscreen grid

"EnTerr" wrote:

In your version of hexToInteger() i notice you have removed a few lines from the original, essentially
    if len(hex_in) mod 2 > 0 then hex_in = "0" + hex_in
I want to caution that not adding that extra 0 in front of a string with odd length will lead to calls like hexToInteger("7") and hexToInteger("100") to return 0, instead of the expected 7 and 256. This is because of how roByteArray.fromHexString(s) behaves with argument which length is not divisible by 2 - something i joked is a "malicious silent failure". Whether you agree with my acerbic humor is not important but make sure your function works with hex strings of both odd and even lengths


I love your humor EnTerr. I must have removed it because I did not think
I needed it for the example. The version I have tucked away is unaltered. But
My apologies it is wrong to recommend something and then alter it. As you can see
I have been working on this far too much. So everything was done impulsively
Time for a break
My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )
0 Kudos