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
"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 .
On cursory look your code only needs the 2nd one.
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
"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
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
"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.
if len(hex_in) mod 2 > 0 then hex_in = "0" + hex_inI 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
"EnTerr" wrote:
In your version of hexToInteger() i notice you have removed a few lines from the original, essentiallyif len(hex_in) mod 2 > 0 then hex_in = "0" + hex_inI 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