Estimado Javier,
Soluciones para el Scroll Táctil en tu Aplicación de Restaurante
Entiendo tu situación. El problema es que TScrDlg no tiene soporte nativo para gestos táctiles, y el xBrowse necesita configuración específica para el scroll kinético. Te presento varias soluciones que encontré en FWH que SÍ funcionan con pantallas touch:
Solución 1: xBrowse con Scroll Kinético (Para tu primera prueba)
Para que el xBrowse funcione correctamente en pantalla táctil, debes asegurarte de que el scroll kinético esté habilitado. El xBrowse tiene una propiedad lKineticBrw que controla esto: 0-cite-0
Esta propiedad se inicializa automáticamente desde una variable de clase: 0-cite-1
Puedes ver un ejemplo funcional de xBrowse con scroll kinético aquí: 0-cite-2
El scroll kinético funciona automáticamente cuando lKineticBrw := .T. (que es el valor por defecto).
Solución 2: TScrollPanelEx (Reemplazo para TScrDlg)
En lugar de TScrDlg, te recomiendo usar TScrollPanelEx, que SÍ tiene soporte completo para gestos táctiles. Esta clase implementa el método HandleGesture() que procesa los gestos de Windows: 0-cite-3
Además, TScrollPanelEx implementa scroll por arrastre (drag): 0-cite-4
Solución 3: TNavigator - IDEAL para Menús de Restaurante ⭐
Encontré una clase que parece diseñada específicamente para menús de restaurante. Mira este ejemplo que usa imágenes de comida (bebidas, platos, pizza, frutas, etc.): 0-cite-5
La clase TNavigator te permite:
- Mostrar imágenes grandes de platillos
- Tener títulos descriptivos
- Expandir/colapsar paneles
- Scroll horizontal suave
- Funciona dentro de un TScrollPanelEx (que tiene soporte táctil completo)
Definición de la clase TNavigator: 0-cite-6
Solución 4: TAlbum - Para Galería de Imágenes
Si prefieres una galería estilo "grid" de imágenes, usa la clase TAlbum: 0-cite-7
TAlbum organiza las imágenes automáticamente en filas y columnas, y tiene scroll vertical suave.
Solución 5: TMetroPanel con TMetroBtn - Estilo Tiles Táctil
Para una interfaz más moderna estilo "tiles" (como Windows 8/10), usa TMetroPanel con botones TMetroBtn: 0-cite-8
Los botones metro soportan:
- Imágenes grandes
- Títulos y descripciones
- Menús desplegables
- Scroll táctil por arrastre 0-cite-9
Implementación con Soporte Táctil en TScrollPanel
Si decides quedarte con botones personalizados, usa TScrollPanel (vertical) en lugar de TScrDlg. Esta clase SÍ tiene soporte para gestos táctiles: 0-cite-10
Y también tiene scroll por arrastre: 0-cite-11
---
Mi Recomendación
Para tu aplicación de restaurante, te sugiero:
- Opción más sencilla: Usa TNavigator con TScrollPanelEx - está prácticamente diseñado para menús de restaurante con imágenes de comida
- Opción moderna: Usa TMetroPanel con TMetroBtn - muy táctil y atractivo visualmente
- Si prefieres xBrowse: Asegúrate de que lKineticBrw esté en .T. (debería estar por defecto)
Notas
- TScrDlg NO tiene soporte táctil - por eso no funciona con los dedos. Es una clase antigua que solo soporta scroll con mouse wheel.
- Las clases TScrollPanel, TScrollPanelEx, TNavigator, TAlbum y TMetroPanel SÍ tienen implementación de gestos táctiles mediante HandleGesture() y/o scroll por arrastre.
- El ejemplo TestNav.prg usa imágenes con nombres en español (bebidas, platos, pizza, fruta, etc.) - parece hecho exactamente para tu caso de uso.
- Todas estas soluciones funcionan en FWH109, ya que son clases que existen desde hace tiempo en el framework.
Citations
File: source/classes/xbrowse.prg (L441-441)
DATA lKineticBrw AS LOGICAL INIT .T.
File: source/classes/xbrowse.prg (L469-469)
CLASSDATA lKinetic AS LOGICAL INIT SetKinetic()
File: samples/misc/kineticx.prg (L1-40)
// Kinetic scroll demo for XBrowse
#include "FiveWin.ch"
#include "XBrowse.ch"
Function Main()
local oLbx, oWnd
local aData := {}
local n
for n = 1 to 1000
AAdd( aData, { "Row: " + StrZero( n, 4 ),;
"Col02:" + StrZero( n, 4 ),;
"Col03:" + StrZero( n, 4 ),;
"Col04:" + StrZero( n, 4 ),;
"Col05:" + StrZero( n, 4 ),;
"Col06:" + StrZero( n, 4 ),;
"Col07:" + StrZero( n, 4 ),;
"Col08:" + StrZero( n, 4 ),;
"Col09:" + StrZero( n, 4 ),;
"Col10:" + StrZero( n, 4 ),;
"Col11:" + StrZero( n, 4 ) } )
next
DEFINE WINDOW oWnd TITLE "KINETIC SCROLL TEST FOR XBROWSE IN " + FWVERSION
@ 0, 0 XBROWSE oLbx OF oWnd ARRAY aData AUTOCOLS LINES
AEval( oLbx:aCols, { |o| o:nWidth := 100 } )
oLbx:nMarqueeStyle := MARQSTYLE_HIGHLROW
oLbx:CreateFromCode()
oWnd:oClient = oLbx
ACTIVATE WINDOW oWnd MAXIMIZED
return nil
File: source/classes/scrollex.prg (L52-58)
METHOD LButtonDown( nRow, nCol, nFlags, lTouch ) INLINE ( ::lScrollDrag := .t., ::nOldCol := nCol, ::Super:LButtonDown( nRow, nCol, nFlags, lTouch ) )
METHOD LButtonUp( nRow, nCol, nFlags ) INLINE ( ::lScrollDrag := .f., ::nOldCol := 0, ::Super:LButtonUp( nRow, nCol, nFlags ) )
METHOD MouseMove( nRow, nCol, nFlags ) INLINE ( ;
if( !::lDrag, ;
( If( lAnd( nFlags, 1 ), nil, ( ::lScrollDrag := .f., ::nOldCol := 0 ) ), ;
If( ::lScrollDrag, ( If( nCol > ::nOldCol, ::GoLeftPix( nCol - ::nOldCol ), ::GoRightPix( ::nOldCol - nCol ) ), ::nOldCol := nCol ), nil ) ), ), ;
::Super:MouseMove( nRow, nCol, nFlags ) )
File: source/classes/scrollex.prg (L434-458)
METHOD HandleGesture( nGesture, nLParam ) CLASS TScrollPanelEx
static nPrevCol := 0
local aInfo, nCol
if nGesture == GID_PAN
aInfo := GESTUREINFO( nLParam )
if aInfo[ 1 ] == GID_PAN
nCol := aInfo[ 4 ]
if aInfo[ 2 ] == GF_BEGIN
nPrevCol := nCol
else
if nCol > nPrevCol
::GoLeftPix( nCol - nPrevCol )
else
::GoRightPix( nPrevCol - nCol )
endif
nPrevCol := nCol
endif
return 0
endif
endif
return ::Super:HandleGesture( nGesture, nLParam )
File: samples/test/TestNav.prg (L56-117)
function TESTPANELS(oScrollPanel, oDlg)
LOCAL nPanelWidth := 200, nPanelHeight:=oScrollPanel:nHeight-25
LOCAL nSpacing := 10
LOCAL i, nLeft
LOCAL cImagePath := cFilePath( GetModuleFileName( GetInstance() ) ) + "..\Bitmaps\pngs\"
LOCAL aImage := {cImagePath+"water.png", cImagePath+"bowl.png", ;
cImagePath+"glass.png", cImagePath+"pancake.png", ;
cImagePath+"pizza.png", cImagePath+"fruit.png", ;
cImagePath+"fish.png", cImagePath+"panini.png", ;
cImagePath+"ice.png", cImagePath+"vegetarian.png"}
LOCAL aPrompts := {"1.bebidas", "2.platos", "3.licores", "4.dulces", "5.pizza", ;
"6.fruta", "7.pez", "8.esfuerzos", "9.helado", "10.vegan"}
local cBtnImage1:=cImagePath+"open.png"
local cBtnImage2:= cImagePath+"close.png"
LOCAL nNavigators := 10
LOCAL aNavigators := {}
local nRow,nCol,n
ASize(aNavigators, nNavigators)
for i := 1 to nNavigators
nLeft := (i - 1) * (nPanelWidth + nSpacing)
aNavigators[i] := TNavigator():New( 0, nLeft, nPanelHeight, nLeft + nPanelWidth, oScrollPanel, ;
.T., CLR_BLACK, , , aPrompts[i], aImage[i],cBtnImage1 ,cBtnImage2,,,,,, .t. )
next
FOR n := 1 TO Len(aNavigators)
aNavigators[n]:aRelatedNavigators := aNavigators
NEXT
aNavigators[1]:CloseAll()
// Imposta il range di scorrimento dopo aver aggiunto i controlli
oScrollPanel:SetRange()
nRow:= oScrollPanel:nHeight+10
nCol:=10
@ nRow, nCol BUTTON "Open" OF oDlg SIZE 90, 30 PIXEL;
ACTION aNavigators[1]:OpenAll() // GotoPanel( aNavigators,oScrollPanel,4)
nCol+=92
@ nRow, nCol BUTTON "close " SIZE 80, 30 PIXEL OF oDlg;
ACTION Collapse(aNavigators,oScrollPanel)
nCol+=84
@ nRow, nCol BUTTON "Go left " SIZE 80, 30 PIXEL OF oDlg;
ACTION oScrollPanel:goleft()
nCol+=84
@ nRow, nCol BUTTON "Go right " SIZE 80, 30 PIXEL OF oDlg;
ACTION oScrollPanel:goright()
nRow+=32
nCol:=10
@ nRow, nCol BUTTON "Goto 4" OF oDlg SIZE 80, 30 PIXEL;
ACTION GotoPanel( aNavigators,oScrollPanel,4)
nCol+=84
@ nRow, nCol BUTTON "Goto 9" OF oDlg SIZE 80, 30 PIXEL;
ACTION GotoPanel( aNavigators,oScrollPanel,9)
return nil
File: source/classes/navpanels.prg (L40-108)
CLASS TNavigator FROM TControl
DATA lExpanded AS LOGICAL INIT .T.
DATA nOriginalWidth AS NUMERIC
DATA nOriginalHeight AS NUMERIC
DATA nWidthClosed AS NUMERIC
DATA nOriginalTop AS NUMERIC
DATA nOriginalLeft AS NUMERIC
DATA cImage
DATA hImage
DATA lBorder AS LOGICAL INIT .F.
DATA nClrBorder
DATA nBackColor HIDDEN
DATA oFont
DATA oBold
DATA oBoldVertical
DATA cTitle
DATA nTitleHeight AS NUMERIC INIT 40
DATA nClrTextSpecial INIT CLR_WHITE
DATA nClrHover INIT RGB(255,69,0)
DATA lOverTitle INIT .F.
DATA lOverPanel INIT .F.
DATA hRegion
DATA nRound INIT 6
DATA lTransparent AS LOGICAL INIT .F.
DATA nOpacity AS NUMERIC INIT 255
DATA nTopColor AS NUMERIC INIT RGB(153,204,255)
DATA nBottomColor AS NUMERIC INIT RGB(0,100,255)
DATA nImageWidth AS NUMERIC INIT 32
DATA nImageHeight AS NUMERIC INIT 32
DATA nZeroZeroClr AS NUMERIC
DATA lGradient AS LOGICAL INIT .T.
DATA oBtnToggle
DATA cBtnImage1
DATA cBtnImage2
DATA lBtnBitmap1 AS LOGICAL INIT .F.
DATA lBtnBitmap2 AS LOGICAL INIT .F.
DATA nTextAlign HIDDEN
DATA aRelatedNavigators
DATA nBetWeenSpace AS NUMERIC INIT 2
DATA bToggled
CLASSDATA lRegistered AS LOGICAL
METHOD New( nTop, nLeft, nBottom, nRight, oWnd, lBorder, nClrBorder,;
nBackColor, nWidthClosed, cTitle, cImage, cBtnImage1, cBtnImage2,;
nClrTextSpecial,nClrHover,nTitleHeight,nBetWeenSpace,lDesign, lPixels ) CONSTRUCTOR
METHOD ReDefine( nId, oWnd, lBorder, nClrBorder,;
nBackColor, nWidthClosed, cTitle, cImage, cBtnImage1, cBtnImage2,;
nClrTextSpecial,nClrHover,nTitleHeight,nBetWeenSpace,lDesign ) CONSTRUCTOR
METHOD CloseAll()
METHOD OpenAll()
METHOD ToggleSize()
METHOD Display() INLINE ::BeginPaint(), ::Paint(), ::EndPaint(), 0
METHOD Resize()
METHOD Paint()
METHOD PaintTitle( hDCMem)
METHOD PaintBorder( hDCMem)
METHOD PaintButton( hDCMem)
METHOD PaintBackImage(hDCMem)
METHOD MouseMove( nRow, nCol, nKeyFlags )
METHOD MouseLeave( nRow, nCol, nFlags )
//METHOD KeyDown( nKey, nFlags )
METHOD LButtonUp( nRow, nCol, nFlags )
METHOD EraseBkGnd( hDC ) INLINE ( ( hDC ), 1 )
METHOD ChangeWidth( nWidth, nHeight )
METHOD Move( nLeft, nTop, nWidth, nHeight, lRepaint )
METHOD Adjust()
METHOD CoorsUpdate()
METHOD Destroy()
ENDCLASS
File: samples/misc/album.prg (L36-94)
CLASS TAlbum
DATA aPhotos
DATA oWnd
DATA nWndWidth INIT 800
DATA nWndHeight INIT 800
DATA nImgWidth INIT 210
DATA nGutter INIT 30
DATA nImgCols PROTECTED
DATA nOffset INIT 0 PROTECTED
DATA nHeight
ACCESS oVScroll INLINE ::oWnd:oVScroll
METHOD New( aPhotos ) CONSTRUCTOR
METHOD Activate()
PROTECTED:
METHOD CreateWindow()
METHOD CreateControls()
METHOD SetVScroll()
METHOD GoUp()
METHOD GoDown()
METHOD ThumbPos( nPos )
METHOD MouseWheel()
METHOD VSetPos() INLINE ::oWnd:oVScroll:SetPos( -::nOffSet )
METHOD GoTop()
METHOD GoBottom()
METHOD KeyDown( nKey )
METHOD ScrollWnd( nPixels )
METHOD Resize( nType, nWidth, nHeight )
ENDCLASS
//----------------------------------------------------------------------------//
METHOD New( aPhotos ) CLASS TAlbum
::aPhotos := aPhotos
return Self
//----------------------------------------------------------------------------//
METHOD Activate() CLASS TAlbum
::CreateWindow()
::CreateControls()
::SetVScroll()
::oWnd:bResized := { |t,w,h| ::Resize( t, w, h ) }
if ::oWnd:IsKindOf( "MDICHILD" )
ACTIVATE WINDOW ::oWnd
else
ACTIVATE WINDOW ::oWnd CENTERED
endif
return Self
File: source/classes/metropnl.prg (L22-80)
CLASS TMetroPanel FROM TPanel
CLASSDATA lRegistered AS LOGICAL
CLASSDATA oActive // for internal use
//
CLASSDATA nBtnSize, nMetroRows, nMetroTop, nMetroMargin, nSliderTop
//
DATA bOnMoveBtn
//
DATA nOffset INIT 0
DATA nScrollRange INIT 0
DATA nScrollRatio INIT 1
DATA oFont, oFontB
DATA oBtnFont, oTextFont
DATA nGroups INIT 1
DATA aButtons INIT Array(0)
DATA lArranged INIT .f.
DATA lDesignMode INIT .f.
DATA nClrScroll
DATA nClrThumb
DATA nMetroWidth, nThumbSize, nThumbWidth
DATA nThumbPos INIT 60
DATA hPen
DATA cTitle
DATA nRow, nCol
DATA oParent
// lDrag, nDragRow, nOldCol used for metro sliding by dragging on screen or scrollbar
DATA lDrag INIT .F.
DATA nDragRow
DATA nOldCol INIT 0
DATA nScrollBarTop
METHOD New( oWnd, cTitle, nClrText, nClrPane, bLClicked, nBtnSize, ;
nClrThumb, nClrScroll ) CONSTRUCTOR
METHOD Paint()
METHOD AddButton( lLarge, nGroup, cCaption, bAction, nClrText, nClrPane, cImgName, oFont, ;
nAlign, nBmpAlign, nBmpWidth, nBmpHeight, cText, nTextAlign, ;
oTextFont, oSubMetro, cBackImage, cAction, cSub )
METHOD Show() INLINE ( ::Arrange(), ::oBrush:ReSize( Self ), ::Super:Show(), ::lVisible := .t. )
METHOD Hide() INLINE ( ::Super:Hide(), ::lVisible := .f. )
METHOD Arrange( lReArrange )
METHOD LButtonDown( nRow, nCol, nFlags, lTouch )
METHOD LButtonUp( nRow, nCol, nFlags )
METHOD MouseMove( nRow, nCol, nFlags )
METHOD MoveBtn( oBtnDrag, oBtnOver )
METHOD SwitchTo( oNext )
METHOD MouseWheel( nKey, nDelta, nXPos, nYPos )
METHOD Slide( nPixels )
METHOD ProgramCode( lShow )
METHOD Destroy()
ENDCLASS
File: source/classes/scrlpanl.prg (L107-115)
METHOD LButtonDown( nRow, nCol, nFlags, lTouch ) INLINE ( ::lScrollDrag := .t., ::nOldRow := nRow, ::nOldCol := nCol, ::Super:LButtonDown( nRow, nCol, nFlags, lTouch ) )
METHOD LButtonUp( nRow, nCol, nFlags ) INLINE ( ::lScrollDrag := .f., ::nOldRow := 0, ::nOldCol := 0, ::Super:LButtonUp( nRow, nCol, nFlags ) )
METHOD MouseMove( nRow, nCol, nFlags ) INLINE ( ;
if( !::lDrag, ;
( If( lAnd( nFlags, 1 ), nil, ( ::lScrollDrag := .f., ::nOldRow := 0, ::nOldCol := 0 ) ), ;
If( ::lScrollDrag, ( If( nRow > ::nOldRow, ::GoUp( nRow - ::nOldRow ), ::GoDown( ::nOldRow - nRow ) ), ;
If( nCol > ::nOldCol, ::GoLeftPix( nCol - ::nOldCol ), ::GoRightPix( ::nOldCol - nCol ) ), ;
::nOldRow := nRow, ::nOldCol := nCol ), nil ) ), ), ;
::Super:MouseMove( nRow, nCol, nFlags ) )
File: source/classes/scrlpanl.prg (L746-771)
METHOD HandleGesture( nGesture, nLParam ) CLASS TScrollPanel
static nPrevRow := 0
local aInfo, nRow
if nGesture == GID_PAN
aInfo := GESTUREINFO( nLParam )
if aInfo[ 1 ] == GID_PAN
nRow := aInfo[ 3 ]
if aInfo[ 2 ] == GF_BEGIN
nPrevRow := nRow
else
if nRow > nPrevRow
::GoUp( nRow - nPrevRow )
else
::GoDown( nPrevRow - nRow )
endif
nPrevRow := nRow
endif
return 0
endif
endif
return ::Super:HandleGesture( nGesture, nLParam )