Bootstrap for FiveWin

Post Reply
User avatar
Otto
Posts: 6396
Joined: Fri Oct 07, 2005 7:07 pm
Has thanked: 8 times
Been thanked: 1 time
Contact:

Bootstrap for FiveWin

Post by Otto »

Hello friends,
Bootstrap for FiveWin is a concept based on the grid system used in modern web frameworks like Bootstrap. It enables the creation of user-friendly and responsive user interfaces (UI) in FiveWin applications. The grid system divides the window into a specific number of columns, allowing for simple and consistent positioning and sizing of controls (such as buttons, text fields, labels, etc.).

Main features:
Grid system: The window is divided into 12 columns, with the spacing between columns (gutter) being definable.

Responsive design: The controls automatically adjust to the size of the window when it is resized.

Easy positioning: Controls can be positioned based on rows and columns, similar to HTML/CSS with Bootstrap.

Flexibility: Controls can span across multiple columns (ColSpan) to create complex layouts.

Code: Select all | Expand

#include "FiveWin.ch"

// Farben für Dark Mode
#DEFINE DARK_BACKGROUND RGB( 40, 40, 40 )
#DEFINE DARK_TEXT RGB( 255, 255, 255 )
#DEFINE DARK_BUTTON_BG RGB( 60, 60, 60 )
#DEFINE DARK_BUTTON_FG RGB( 255, 255, 255 )

// Hauptprogramm
FUNCTION Main()
  
      LOCAL oDlg, oGrid
      LOCAL oBtnOk, oBtnCancel

      // Dialogfenster erstellen
      DEFINE DIALOG oDlg ;
         TITLE "FiveWin Bootstrap Test" ;
         SIZE 800, 600 ;
         TRUEPIXEL ;
         STYLE WS_THICKFRAME

      oDlg:SetColor( DARK_TEXT, DARK_BACKGROUND )

      // Grid-System erstellen (12 Spalten, 10 Pixel Abstand)
      oGrid := FWGrid():New( 12, 10 )

      // Buttons hinzufügen
      oBtnOk := FWButton():New( "&Ok", { || MsgInfo("Ok clicked!") }, oDlg, 1, 1, 3 )
      oBtnOk:SetColor( DARK_BUTTON_FG, DARK_BUTTON_BG )
      oGrid:AddControl( oBtnOk:oButton, 1, 1, 3 )

      oBtnCancel := FWButton():New( "&Cancel", { || oDlg:End() }, oDlg, 1, 4, 3 )
      oBtnCancel:SetColor( DARK_BUTTON_FG, DARK_BUTTON_BG )
      oGrid:AddControl( oBtnCancel:oButton, 1, 4, 3 )

      // Grid-System dem Dialog zuweisen
      oDlg:bResized := { || oGrid:Resize( oDlg:nWidth(), oDlg:nHeight() ) }

      // Dialogfenster anzeigen
      ACTIVATE DIALOG oDlg ;
         CENTERED

  
   RETURN NIL

// Grid-System
CLASS FWGrid
   DATA aControls
   DATA nColumns
   DATA nGutter

   METHOD New( nColumns, nGutter )
   METHOD AddControl( oControl, nRow, nCol, nColSpan )
   METHOD Resize( nWidth, nHeight )
ENDCLASS

METHOD New( nColumns, nGutter ) CLASS FWGrid
   ::aControls := {}
   ::nColumns := nColumns
   ::nGutter := nGutter
   RETURN Self

METHOD AddControl( oControl, nRow, nCol, nColSpan ) CLASS FWGrid
   AAdd( ::aControls, { oControl, nRow, nCol, nColSpan } )
   RETURN Self

METHOD Resize( nWidth, nHeight ) CLASS FWGrid
   LOCAL nColWidth := (nWidth - (::nColumns + 1) * ::nGutter) / ::nColumns
   LOCAL nControlWidth, nControlHeight, nX, nY
   LOCAL aControl

   FOR EACH aControl IN ::aControls
      IF aControl[1] != NIL
         nControlWidth := nColWidth * aControl[4] + (aControl[4] - 1) * ::nGutter
         nControlHeight := 32  // Standardhöhe
         nX := ::nGutter + (aControl[3] - 1) * (nColWidth + ::nGutter)
         nY := ::nGutter + (aControl[2] - 1) * (nControlHeight + ::nGutter)

         aControl[1]:SetPos( nY, nX )
         aControl[1]:SetSize( nControlWidth, nControlHeight )
      ENDIF
   NEXT
   RETURN Self

// Vordefinierte Klassen
CLASS FWButton
   DATA oButton

   METHOD New( cCaption, bAction, oWnd, nRow, nCol, nColSpan )
   METHOD SetColor( nTextColor, nBgColor )
ENDCLASS

METHOD New( cCaption, bAction, oWnd, nRow, nCol, nColSpan ) CLASS FWButton
   ::oButton := TButton():New( 0, 0, cCaption, oWnd, bAction, 100, 32 )
   RETURN Self

METHOD SetColor( nTextColor, nBgColor ) CLASS FWButton
   ::oButton:SetColor( nTextColor, nBgColor )
   RETURN Self

Then I thought that it might be better to simply extend the TWindows class.
But I still have problems here.
Best regards,
Otto

class

Code: Select all | Expand

CLASS TWindow
   // ... (vorhandene Daten und Methoden)

   // Neue Eigenschaften für das Grid-System
   DATA nColumns AS NUMERIC INIT 12  // Anzahl der Spalten im Grid
   DATA nGutter  AS NUMERIC INIT 5   // Abstand zwischen den Steuerelementen
   DATA oGrid    AS ARRAY   INIT {}  // Array zur Speicherung der Steuerelemente im Grid

   // Neue Methoden für das Grid-System
   METHOD AddToGrid( oControl, nRow, nCol, nColSpan )
   METHOD ResizeControls( nWidth, nHeight )
   METHOD _NCOLUMNS( nNewValue ) SETGET

   // ... (restliche vorhandene Methoden)
ENDCLASS

//----------------------------------------------------------------------------//

METHOD AddToGrid( oControl, nRow, nCol, nColSpan ) CLASS TWindow
   // Fügt ein Steuerelement zum Grid hinzu
   IF !EMPTY( ::oGrid )
      AAdd( ::oGrid, { oControl, nRow, nCol, nColSpan } )
   ENDIF
   RETURN Self

//----------------------------------------------------------------------------//

METHOD ResizeControls( nWidth, nHeight ) CLASS TWindow
   // Berechnet die Größe und Position der Steuerelemente basierend auf dem Grid
   LOCAL nColWidth := (nWidth - (::nColumns + 1) * ::nGutter) / ::nColumns
   LOCAL nControlWidth, nControlHeight, nX, nY
   LOCAL aControl

   IF !EMPTY( ::oGrid )
      FOR EACH aControl IN ::oGrid
         IF aControl[1] != NIL
            nControlWidth := nColWidth * aControl[4] + (aControl[4] - 1) * ::nGutter
            nControlHeight := 32  // Standardhöhe
            nX := ::nGutter + (aControl[3] - 1) * (nColWidth + ::nGutter)
            nY := ::nGutter + (aControl[2] - 1) * (nControlHeight + ::nGutter)

            aControl[1]:SetPos( nY, nX )
            aControl[1]:SetSize( nControlWidth, nControlHeight )
         ENDIF
      NEXT
   ENDIF
   RETURN Self

//----------------------------------------------------------------------------//

METHOD _NCOLUMNS( nNewValue ) CLASS TWindow
   IF PCount() > 0
      ::nColumns := nNewValue
   ENDIF
   RETURN ::nColumns

prg

Code: Select all | Expand

FUNCTION Main()
   LOCAL oDlg
   LOCAL oSayName, oGetName
   LOCAL oSayAge, oGetAge
   LOCAL oBtnOk, oBtnCancel

   // Create the dialog
   oDlg := TDialog():New(,,,, "FiveWin Bootstrap Test",,, .F., 262144,,,,, .F.,,,, 800, 600, .F.,, "oDlg", nil, .T., )

   // Initialize grid system
   oDlg:nColumns := 12
   oDlg:nGutter := 5
   oDlg:oGrid := {}

   // SAY (Label) for Name
   oSayName := TSay():New( 0, 0, "Name:", oDlg )
   oDlg:AddToGrid( oSayName, 2, 1, 2 )

   // GET (Input field) for Name
   oGetName := TGet():New( 0, 0, 100, 32, "", oDlg )
   oDlg:AddToGrid( oGetName, 2, 3, 4 )

   // SAY (Label) for Age
   oSayAge := TSay():New( 0, 0, "Alter:", oDlg )
   oDlg:AddToGrid( oSayAge, 3, 1, 2 )

   // GET (Input field) for Age
   oGetAge := TGet():New( 0, 0, 100, 32, "", oDlg )
   oDlg:AddToGrid( oGetAge, 3, 3, 4 )

   // Buttons
   oBtnOk := TButton():New( 0, 0, "&Ok", oDlg, { || MsgInfo("Ok clicked!") }, 100, 32 )
   oDlg:AddToGrid( oBtnOk, 4, 1, 3 )

   oBtnCancel := TButton():New( 0, 0, "&Cancel", oDlg, { || oDlg:End() }, 100, 32 )
   oDlg:AddToGrid( oBtnCancel, 4, 4, 3 )

   // Assign grid system to the dialog
   oDlg:bResized := { || oDlg:ResizeControls( oDlg:nWidth(), oDlg:nHeight() ) }

   // Show the dialog
   ACTIVATE DIALOG oDlg CENTERED

   RETURN NIL
   
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
Posts: 6396
Joined: Fri Oct 07, 2005 7:07 pm
Has thanked: 8 times
Been thanked: 1 time
Contact:

Re: Bootstrap for FiveWin

Post by Otto »

Source update - breakpoint

Code: Select all | Expand

#include "FiveWin.ch"

// Farben für Dark Mode
#DEFINE DARK_BACKGROUND RGB( 40, 40, 40 )
#DEFINE DARK_TEXT RGB( 255, 255, 255 )
#DEFINE DARK_BUTTON_BG RGB( 60, 60, 60 )
#DEFINE DARK_BUTTON_FG RGB( 255, 255, 255 )

// Hauptprogramm
FUNCTION Main()
  
      LOCAL oDlg, oGrid
      LOCAL oBtnOk, oBtnCancel

      // Dialogfenster erstellen
      DEFINE DIALOG oDlg ;
         TITLE "FiveWin Bootstrap Test" ;
         SIZE 800, 600 ;
         TRUEPIXEL ;
         STYLE WS_THICKFRAME

      oDlg:SetColor( DARK_TEXT, DARK_BACKGROUND )

      // Grid-System erstellen (12 Spalten, 10 Pixel Abstand)
      oGrid := FWGrid():New( 12, 10 )

      // Buttons hinzufügen
      oBtnOk := FWButton():New( "&Ok", { || MsgInfo("Ok clicked!") }, oDlg, 1, 1, 3 )
      oBtnOk:SetColor( DARK_BUTTON_FG, DARK_BUTTON_BG )
      oGrid:AddControl( oBtnOk:oButton, 1, 1, 3 )

      oBtnCancel := FWButton():New( "&Cancel", { || oDlg:End() }, oDlg, 1, 4, 3 )
      oBtnCancel:SetColor( DARK_BUTTON_FG, DARK_BUTTON_BG )
      oGrid:AddControl( oBtnCancel:oButton, 1, 4, 3 )

      // Grid-System dem Dialog zuweisen
      oDlg:bResized := { || oGrid:Resize( oDlg:nWidth(), oDlg:nHeight() ) }

      // Dialogfenster anzeigen
      ACTIVATE DIALOG oDlg ;
         CENTERED

  
   RETURN NIL

// Grid-System
CLASS FWGrid
   DATA aControls
   DATA nColumns
   DATA nGutter

   METHOD New( nColumns, nGutter )
   METHOD AddControl( oControl, nRow, nCol, nColSpan )
   METHOD Resize( nWidth, nHeight )
ENDCLASS

METHOD New( nColumns, nGutter ) CLASS FWGrid
   ::aControls := {}
   ::nColumns := nColumns
   ::nGutter := nGutter
   RETURN Self

METHOD AddControl( oControl, nRow, nCol, nColSpan ) CLASS FWGrid
   AAdd( ::aControls, { oControl, nRow, nCol, nColSpan } )
   RETURN Self

METHOD Resize( nWidth, nHeight ) CLASS FWGrid
   LOCAL nColWidth := (nWidth - (::nColumns + 1) * ::nGutter) / ::nColumns
   LOCAL nControlWidth, nControlHeight, nX, nY
   LOCAL aControl
   LOCAL nPrevY := ::nGutter  // Startposition für die erste Zeile
   LOCAL nBreakpoint := 400   // Schwellenwert für das Stapeln der Schalter

   // Wenn die Fensterbreite kleiner als der Breakpoint ist, stapeln wir die Schalter vertikal
   IF nWidth < nBreakpoint
      FOR EACH aControl IN ::aControls
         IF aControl[1] != NIL
            nControlWidth := nWidth - 2 * ::nGutter  // Volle Breite minus Gutter
            nControlHeight := 32  // Standardhöhe
            nX := ::nGutter  // Linksbündig
            nY := nPrevY     // Verwende die vorherige Y-Position

            aControl[1]:SetPos( nY, nX )
            aControl[1]:SetSize( nControlWidth, nControlHeight )

            nPrevY += nControlHeight + ::nGutter  // Aktualisiere die Y-Position für das nächste Steuerelement
         ENDIF
      NEXT
   ELSE
      // Normales Grid-Layout (Schalter nebeneinander)
      FOR EACH aControl IN ::aControls
         IF aControl[1] != NIL
            nControlWidth := nColWidth * aControl[4] + (aControl[4] - 1) * ::nGutter
            nControlHeight := 32  // Standardhöhe
            nX := ::nGutter + (aControl[3] - 1) * (nColWidth + ::nGutter)
            nY := ::nGutter  // Alle Schalter in derselben Zeile

            aControl[1]:SetPos( nY, nX )
            aControl[1]:SetSize( nControlWidth, nControlHeight )
         ENDIF
      NEXT
   ENDIF
   RETURN Self

// Vordefinierte Klassen
CLASS FWButton
   DATA oButton

   METHOD New( cCaption, bAction, oWnd, nRow, nCol, nColSpan )
   METHOD SetColor( nTextColor, nBgColor )
ENDCLASS

METHOD New( cCaption, bAction, oWnd, nRow, nCol, nColSpan ) CLASS FWButton
   ::oButton := TButton():New( 0, 0, cCaption, oWnd, bAction, 100, 32 )
   RETURN Self

METHOD SetColor( nTextColor, nBgColor ) CLASS FWButton
   ::oButton:SetColor( nTextColor, nBgColor )
   RETURN Self

********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
Posts: 6396
Joined: Fri Oct 07, 2005 7:07 pm
Has thanked: 8 times
Been thanked: 1 time
Contact:

Re: Bootstrap for FiveWin

Post by Otto »

Update:

Image


Code: Select all | Expand

 
#include "FiveWin.ch"
#ifdef __XHARBOUR__
       /* Hash item functions */
       #xtranslate hb_Hash( [<x,...>] )            => Hash( <x> )
       #xtranslate hb_HHasKey( [<x,...>] )         => HHasKey( <x> )
       #xtranslate hb_HPos( [<x,...>] )            => HGetPos( <x> )
       #xtranslate hb_HGet( [<x,...>] )            => HGet( <x> )
       #xtranslate hb_HSet( [<x,...>] )            => HSet( <x> )
       #xtranslate hb_HDel( [<x,...>] )            => HDel( <x> )
       #xtranslate hb_HKeyAt( [<x,...>] )          => HGetKeyAt( <x> )
       #xtranslate hb_HValueAt( [<x,...>] )        => HGetValueAt( <x> )
       #xtranslate hb_HValueAt( [<x,...>] )        => HSetValueAt( <x> )
       #xtranslate hb_HPairAt( [<x,...>] )         => HGetPairAt( <x> )
       #xtranslate hb_HDelAt( [<x,...>] )          => HDelAt( <x> )
       #xtranslate hb_HKeys( [<x,...>] )           => HGetKeys( <x> )
       #xtranslate hb_HValues( [<x,...>] )         => HGetValues( <x> )
       #xtranslate hb_HFill( [<x,...>] )           => HFill( <x> )
       #xtranslate hb_HClone( [<x,...>] )          => HClone( <x> )
       #xtranslate hb_HCopy( [<x,...>] )           => HCopy( <x> )
       #xtranslate hb_HMerge( [<x,...>] )          => HMerge( <x> )
       #xtranslate hb_HEval( [<x,...>] )           => HEval( <x> )
       #xtranslate hb_HScan( [<x,...>] )           => HScan( <x> )
       #xtranslate hb_HSetCaseMatch( [<x,...>] )   => HSetCaseMatch( <x> )
       #xtranslate hb_HCaseMatch( [<x,...>] )      => HGetCaseMatch( <x> )
       #xtranslate hb_HSetAutoAdd( [<x,...>] )     => HSetAutoAdd( <x> )
       #xtranslate hb_HAutoAdd( [<x,...>] )        => HGetAutoAdd( <x> )
       #xtranslate hb_HAllocate( [<x,...>] )       => HAllocate( <x> )
       #xtranslate hb_HDefault( [<x,...>] )        => HDefault( <x> )
    #else
       /* Hash item functions */
       #xtranslate Hash( [<x,...>] )           => hb_Hash( <x> )
       #xtranslate HHasKey( [<x,...>] )        => hb_HHasKey( <x> )
       #xtranslate HGetPos( [<x,...>] )        => hb_HPos( <x> )
       #xtranslate HGet( [<x,...>] )           => hb_HGet( <x> )
       #xtranslate HSet( [<x,...>] )           => hb_HSet( <x> )
       #xtranslate HDel( [<x,...>] )           => hb_HDel( <x> )
       #xtranslate HGetKeyAt( [<x,...>] )      => hb_HKeyAt( <x> )
       #xtranslate HGetValueAt( [<x,...>] )    => hb_HValueAt( <x> )
       #xtranslate HSetValueAt( [<x,...>] )    => hb_HValueAt( <x> )
       #xtranslate HGetPairAt( [<x,...>] )     => hb_HPairAt( <x> )
       #xtranslate HDelAt( [<x,...>] )         => hb_HDelAt( <x> )
       #xtranslate HGetKeys( [<x,...>] )       => hb_HKeys( <x> )
       #xtranslate HGetValues( [<x,...>] )     => hb_HValues( <x> )
       #xtranslate HFill( [<x,...>] )          => hb_HFill( <x> )
       #xtranslate HClone( [<x,...>] )         => hb_HClone( <x> )
       #xtranslate HCopy( [<x,...>] )          => hb_HCopy( <x> )
       #xtranslate HMerge( [<x,...>] )         => hb_HMerge( <x> )
       #xtranslate HEval( [<x,...>] )          => hb_HEval( <x> )
       #xtranslate HScan( [<x,...>] )          => hb_HScan( <x> )
       #xtranslate HSetCaseMatch( [<x,...>] )  => hb_HSetCaseMatch( <x> )
       #xtranslate HGetCaseMatch( [<x,...>] )  => hb_HCaseMatch( <x> )
       #xtranslate HSetAutoAdd( [<x,...>] )    => hb_HSetAutoAdd( <x> )
       #xtranslate HGetAutoAdd( [<x,...>] )    => hb_HAutoAdd( <x> )
       #xtranslate HAllocate( [<x,...>] )      => hb_HAllocate( <x> )
       #xtranslate HDefault( [<x,...>] )       => hb_HDefault( <x> )
       #xtranslate HSetPartition( [<x,...>] )  =>

       /* Associative hash array functions */
       #xtranslate haAGetKeyAt( [<x,...>] )    => hb_HKeyAt( <x> )
       #xtranslate haAGetValueAt( [<x,...>] )  => hb_HValueAt( <x> )
       #xtranslate haADelAt( [<x,...>] )       => hb_HDelAt( <x> )
       #xtranslate haAGetPos( [<x,...>] )      => hb_HPos( <x> )
       #xtranslate haAGetRealPos( <x>, <y> )   => iif( HB_ISNUMERIC( <y> ) .AND. <y> >= 1 .AND. ;
                                                       Int( <y> ) <= Len( <x> ), Int( <y> ), 0 )
       #xtranslate HGetVaaPos( <x> )           => {| h | ;;
                                                    LOCAL a := Array( Len( h ), v ;;
                                                    FOR EACH v IN a ;;
                                                       v := v:__enumIndex() ;;
                                                    NEXT ;;
                                                    RETURN a ; }:eval( <x> )
       #xtranslate HGetAACompatibility( <x> )  => hb_HKeepOrder( <x> )
       #xtranslate HSetAACompatibility( [<x,...>] ) => {| h | ;;
                                                       hb_HKeepOrder( h ) ;;
                                                       RETURN .T. ; }:eval( <x> )

    #endif





// Farben für Dark Mode
#DEFINE DARK_BACKGROUND RGB( 40, 40, 40 )
#DEFINE DARK_TEXT RGB( 255, 255, 255 )
#DEFINE DARK_BUTTON_BG RGB( 60, 60, 60 )
#DEFINE DARK_BUTTON_FG RGB( 255, 255, 255 )
#DEFINE DARK_GET_BG RGB( 80, 80, 80 )
#DEFINE DARK_GET_FG RGB( 255, 255, 255 )

// Hauptprogramm

static oDlg
FUNCTION Main()
  
   LOCAL  oGrid
   LOCAL oBtnOk, oBtnCancel
   LOCAL oSayTitle, oSayName, oGetName, oSayEmail, oGetEmail
   LOCAL cName := "", cEmail := ""

   // Dialogfenster erstellen
   DEFINE DIALOG oDlg ;
      TITLE "FiveWin Bootstrap Test" ;
      SIZE 800, 600 ;
      TRUEPIXEL ;
      STYLE WS_THICKFRAME

   oDlg:SetColor( DARK_TEXT, DARK_BACKGROUND )

   // Grid-System erstellen (12 Spalten, 10 Pixel Abstand)
   oGrid := FWGrid():New( 12, 10 )

   // Titel SAY (responsiv)
   oSayTitle := TSay():New( 10, 10, { || "Bootstrap grid like system" }, oDlg )
   oSayTitle:SetColor( DARK_TEXT, DARK_BACKGROUND )
   oSayTitle:SetFont( TFont():New( "Arial", 0, 24 ) )  // Große Schriftgröße für den Titel
   oGrid:AddControl( oSayTitle, 1, 1, 12 )  // Titel über die gesamte Breite
 

	
	// SAY und GET für Name
	oSayName := TSay():New( 50, 10, { || "Name:" }, oDlg )
	oSayName:SetColor( DARK_TEXT, DARK_BACKGROUND )
	oSayName:SetFont( TFont():New( "Arial", 0, 18 ) )
	oGrid:AddControl( oSayName, 2, 1, 2 )
	
	// Name-Get rein über TGet():New()
	oGetName := TGet():New( 50, 100, { || cName }, oDlg )
	oGetName:SetSize( 200, 32 )
	oGetName:SetColor( DARK_GET_FG, DARK_GET_BG )
	oGrid:AddControl( oGetName, 2, 3, 6 )
	
	// SAY und GET für E-Mail
	oSayEmail := TSay():New( 90, 10, { || "E-Mail:" }, oDlg )
	oSayEmail:SetColor( DARK_TEXT, DARK_BACKGROUND )
	oSayEmail:SetFont( TFont():New( "Arial", 0, 18 ) )
	oGrid:AddControl( oSayEmail, 3, 1, 2 )
	
	// E-Mail-Get (Korrektur: { || cEmail } statt { || cName }, andere Zeile)
	oGetEmail := TGet():New( 90, 100, { || cEmail }, oDlg )
	oGetEmail:SetSize( 200, 32 )
	oGetEmail:SetColor( DARK_GET_FG, DARK_GET_BG )
	oGrid:AddControl( oGetEmail, 3, 3, 6 )

 
   
   
   
   

   // Buttons hinzufügen
   oBtnOk := FWButton():New( "&Ok", { || MsgInfo("Ok clicked!") }, oDlg, 4, 1, 3 )
   oBtnOk:SetColor( DARK_BUTTON_FG, DARK_BUTTON_BG )
   oGrid:AddControl( oBtnOk:oButton, 4, 1, 3 )

   oBtnCancel := FWButton():New( "&Cancel", { || oDlg:End() }, oDlg, 4, 4, 3 )
   oBtnCancel:SetColor( DARK_BUTTON_FG, DARK_BUTTON_BG )
   oGrid:AddControl( oBtnCancel:oButton, 4, 4, 3 )

   // Grid-System dem Dialog zuweisen
   oDlg:bResized := ( { || oGrid:Resize( oDlg:nWidth(), oDlg:nHeight() ) } )

   // Dialogfenster anzeigen
   ACTIVATE DIALOG oDlg ;
      CENTERED

   RETURN NIL

// Grid-System
CLASS FWGrid
   DATA aControls
   DATA nColumns
   DATA nGutter

   METHOD New( nColumns, nGutter )
   METHOD AddControl( oControl, nRow, nCol, nColSpan )
   METHOD Resize( nWidth, nHeight )
ENDCLASS

METHOD New( nColumns, nGutter ) CLASS FWGrid
   ::aControls := {}
   ::nColumns := nColumns
   ::nGutter := nGutter
   RETURN Self

METHOD AddControl( oControl, nRow, nCol, nColSpan ) CLASS FWGrid
   AAdd( ::aControls, { oControl, nRow, nCol, nColSpan } )
   RETURN Self


METHOD Resize( nWidth, nHeight ) CLASS FWGrid
   LOCAL nColWidth := (nWidth - (::nColumns + 1) * ::nGutter) / ::nColumns
   LOCAL nControlWidth, nControlHeight, nX, nY
   LOCAL aControl
   LOCAL nPrevY := ::nGutter  // Startposition für die erste Zeile
   LOCAL nBreakpoint := 600   // Schwellenwert für das Stapeln der Controls
   LOCAL hRows := Hash()      // Initialisiere hRows als Hash
   LOCAL nRowKey, aRowControls
	LOCAL aKeys  
        

   // Wenn die Fensterbreite kleiner als der Breakpoint ist, stapeln wir die Controls vertikal
   IF nWidth < nBreakpoint
      // Hilfsarray: wir gruppieren Controls nach Row

      // Schritt 1: alle Controls entsprechend ihrer nRow einsortieren
      FOR EACH aControl IN ::aControls
         IF !HB_HHasKey( hRows, aControl[2] )
            hRows[ aControl[2] ] := {}  // Initialisiere eine leere Array-Liste für jede Zeile
         ENDIF
         AAdd( hRows[ aControl[2] ], aControl )
      NEXT

      // Schritt 2: pro Row einen "Block" untereinander anzeigen
       
			aKeys := HB_HKeys( hRows )
			ASort( aKeys )            // sortierte Reihenfolge
			FOR EACH nRowKey IN aKeys // jetzt garantiert Row 1,2,3,...

     
          aRowControls := hRows[ nRowKey ]

          // Sortiere die Controls innerhalb jeder Zeile nach ihrer Spaltenposition (nCol)
          ASort( aRowControls,,, { |a, b| a[3] < b[3] } )
          FOR EACH aControl IN aRowControls
          NEXT

          FOR EACH aControl IN aRowControls
             nControlWidth := nWidth - 2 * ::nGutter  // Volle Breite minus Gutter
             nControlHeight := 32  // Standardhöhe
             nX := ::nGutter  // Linksbündig

             // Titel: Schriftgröße drastisch verkleinern
             IF aControl[1]:ClassName() == "TSAY" .AND. aControl[2] == 1
                aControl[1]:SetFont( TFont():New( "Arial", 0, 16 ) )  // Kleinere Schriftgröße
                aControl[1]:SetPos( nPrevY, nX )
                aControl[1]:SetSize( nControlWidth, nControlHeight )
                nPrevY += nControlHeight + ::nGutter  // Nächste Zeile
             ENDIF

             // SAY: Schriftgröße drastisch verkleinern
             IF aControl[1]:ClassName() == "TSAY" .AND. aControl[2] != 1
                aControl[1]:SetFont( TFont():New( "Arial", 0, 12 ) )  // Kleinere Schriftgröße
                aControl[1]:SetPos( nPrevY, nX )
                aControl[1]:SetSize( nControlWidth, nControlHeight )
                nPrevY += nControlHeight + ::nGutter  // Nächste Zeile für GET
             ENDIF

             // GET: In der darunterliegenden Zeile
             IF aControl[1]:ClassName() == "TGET"
              //  aControl[1]:SetPos( nPrevY, nX )
                
              
              
              aControl[1]:nTop  := nPrevY
              aControl[1]:nLeft  := nX
     
              
                aControl[1]:SetSize( nControlWidth, nControlHeight )
                nPrevY += nControlHeight + ::nGutter  // Nächste Zeile für das nächste SAY
             ENDIF

             // Buttons: Untereinander, Cancel linksbündig
             IF aControl[1]:ClassName() == "TBUTTON"
                aControl[1]:SetPos( nPrevY, nX )
                
                aControl[1]:SetSize( nControlWidth, nControlHeight )
                nPrevY += nControlHeight + ::nGutter  // Nächste Zeile für den nächsten Button
             ENDIF

             // Erzwinge eine Aktualisierung des Controls
             aControl[1]:Refresh()
          NEXT
      NEXT
   ELSE
      // Normales Grid-Layout (SAY und GET nebeneinander, Buttons nebeneinander)
      FOR EACH aControl IN ::aControls
         IF aControl[1] != NIL
            nControlWidth := nColWidth * aControl[4] + (aControl[4] - 1) * ::nGutter
            nControlHeight := 32  // Standardhöhe
            nX := ::nGutter + (aControl[3] - 1) * (nColWidth + ::nGutter)
            nY := ::nGutter + (aControl[2] - 1) * (nControlHeight + ::nGutter)

            // Titel: Schriftgröße zurücksetzen
            IF aControl[1]:ClassName() == "TSAY" .AND. aControl[2] == 1
               aControl[1]:SetFont( TFont():New( "Arial", 0, 26 ) )  // Große Schriftgröße
            ENDIF

            // SAY: Schriftgröße zurücksetzen
            IF aControl[1]:ClassName() == "TSAY" .AND. aControl[2] != 1
               aControl[1]:SetFont( TFont():New( "Arial", 0, 18 ) )  // Normale Schriftgröße
            ENDIF
            
             IF aControl[1]:ClassName() == "TGET"
              	aControl[1]:nTop  := nY
              aControl[1]:nLeft  := nX
             ELSE
             	aControl[1]:SetPos( nY, nX )
             ENDIF
            
            
            aControl[1]:SetSize( nControlWidth, nControlHeight )
            aControl[1]:Refresh()
         ENDIF
      NEXT
   ENDIF
   RETURN Self


   
// Vordefinierte Klassen
CLASS FWButton
   DATA oButton

   METHOD New( cCaption, bAction, oWnd, nRow, nCol, nColSpan )
   METHOD SetColor( nTextColor, nBgColor )
ENDCLASS

METHOD New( cCaption, bAction, oWnd, nRow, nCol, nColSpan ) CLASS FWButton
   ::oButton := TButton():New( 0, 0, cCaption, oWnd, bAction, 100, 32 )
   RETURN Self

METHOD SetColor( nTextColor, nBgColor ) CLASS FWButton
   ::oButton:SetColor( nTextColor, nBgColor )
   RETURN Self
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
csincuir
Posts: 412
Joined: Sat Feb 03, 2007 6:36 am
Location: Guatemala
Contact:

Re: Bootstrap for FiveWin

Post by csincuir »

Interesting work Otto
I don't know if you have thought about changing the color theme, I don't really like the dark theme, so I made this change to your development to change the theme, suddenly you adapt it to your work:

Code: Select all | Expand

#include "FiveWin.ch"

#DEFINE DARK_THEME  {;
   "background" => RGB( 40, 40, 40 ),;
   "text"       => RGB( 255, 255, 255 ),;
   "button_bg"  => RGB( 60, 60, 60 ),;
   "button_fg"  => RGB( 255, 255, 255 );
}

#DEFINE LIGHT_THEME {;
   "background" => RGB( 255, 255, 255 ),;
   "text"       => RGB( 0, 0, 0 ),;
   "button_bg"  => RGB( 240, 240, 240 ),;
   "button_fg"  => RGB( 0, 0, 0 );
}

FUNCTION Main()
   LOCAL oDlg, oGrid
   LOCAL oBtnOk, oBtnCancel, oBtnToggleTheme

   DEFINE DIALOG oDlg ;
      TITLE "FiveWin Bootstrap Test" ;
      SIZE 800, 600 ;
      TRUEPIXEL ;
      STYLE WS_THICKFRAME

   oGrid := FWGrid():New( 12, 10, oDlg )

   oBtnOk := FWButton():New( "&Ok", { || MsgInfo("Ok clicked!") }, oDlg, 1, 1, 3 )
   oGrid:AddControl( oBtnOk:oButton, 1, 1, 3 )

   oBtnCancel := FWButton():New( "&Cancel", { || oDlg:End() }, oDlg, 1, 4, 3 )
   oGrid:AddControl( oBtnCancel:oButton, 1, 4, 3 )

   oBtnToggleTheme := FWButton():New( "Toggle Theme", { || oGrid:ToggleTheme() }, oDlg, 2, 1, 3 )
   oGrid:AddControl( oBtnToggleTheme:oButton, 2, 1, 3 )

   oDlg:bResized := { || oGrid:Resize( oDlg:nWidth(), oDlg:nHeight() ) }

   // Aplicar el tema oscuro por defecto
   oGrid:SetTheme( LIGHT_THEME )
   oGrid:ToggleTheme()

   ACTIVATE DIALOG oDlg ;
      CENTERED

RETURN NIL

CLASS FWGrid
   DATA aControls
   DATA nColumns
   DATA nGutter
   DATA oDlg

   METHOD New( nColumns, nGutter, oDlg )
   METHOD AddControl( oControl, nRow, nCol, nColSpan )
   METHOD Resize( nWidth, nHeight )
   METHOD SetTheme( aTheme )
   METHOD ToggleTheme()
ENDCLASS

METHOD New( nColumns, nGutter, oDlg ) CLASS FWGrid
   ::aControls := {}
   ::nColumns := nColumns
   ::nGutter := nGutter
   ::oDlg := oDlg
   RETURN Self

METHOD AddControl( oControl, nRow, nCol, nColSpan ) CLASS FWGrid
   AAdd( ::aControls, { oControl, nRow, nCol, nColSpan } )
   RETURN Self

METHOD Resize( nWidth, nHeight ) CLASS FWGrid
   LOCAL nColWidth := (nWidth - (::nColumns + 1) * ::nGutter) / ::nColumns
   LOCAL nControlWidth, nControlHeight, nX, nY
   LOCAL aControl

   FOR EACH aControl IN ::aControls
      IF aControl[1] != NIL
         nControlWidth := nColWidth * aControl[4] + (aControl[4] - 1) * ::nGutter
         nControlHeight := 32
         nX := ::nGutter + (aControl[3] - 1) * (nColWidth + ::nGutter)
         nY := ::nGutter + (aControl[2] - 1) * (nControlHeight + ::nGutter)

         aControl[1]:SetPos( nY, nX )
         aControl[1]:SetSize( nControlWidth, nControlHeight )
      ENDIF
   NEXT
   RETURN Self

METHOD SetTheme( aTheme ) CLASS FWGrid
   LOCAL aControl

   // Cambiar el color de fondo de la ventana
   ::oDlg:SetColor( aTheme["text"], aTheme["background"] )

   // Aplicar el tema a todos los controles
   FOR EACH aControl IN ::aControls
      IF aControl[1] != NIL
         IF aControl[1]:ClassName() == "TBUTTON"  // Si es un botón
            aControl[1]:SetColor( aTheme["button_fg"], aTheme["button_bg"] )
         ENDIF
         // Aquí puedes agregar más condiciones para otros tipos de controles
      ENDIF
   NEXT
   
   ::oDlg:Refresh()
   
   RETURN Self

METHOD ToggleTheme() CLASS FWGrid
   STATIC lDarkTheme := .T.

   IF lDarkTheme
      ::SetTheme( LIGHT_THEME )
   ELSE
      ::SetTheme( DARK_THEME )
   ENDIF

   lDarkTheme := !lDarkTheme

   RETURN Self

CLASS FWButton
   DATA oButton

   METHOD New( cCaption, bAction, oWnd, nRow, nCol, nColSpan )
   METHOD SetColor( nTextColor, nBgColor )
   METHOD SetTheme( aTheme )
ENDCLASS

METHOD New( cCaption, bAction, oWnd, nRow, nCol, nColSpan ) CLASS FWButton
   ::oButton := TButton():New( 0, 0, cCaption, oWnd, bAction, 100, 32 )
   RETURN Self

METHOD SetColor( nTextColor, nBgColor ) CLASS FWButton
   ::oButton:SetColor( nTextColor, nBgColor )
   RETURN Self

METHOD SetTheme( aTheme ) CLASS FWButton
   ::SetColor( aTheme["button_fg"], aTheme["button_bg"] )
   RETURN Self
Best regards.

Carlos
User avatar
Antonio Linares
Site Admin
Posts: 42393
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Has thanked: 9 times
Been thanked: 41 times
Contact:

Re: Bootstrap for FiveWin

Post by Antonio Linares »

Just an idea:

If you use FWH Class TWebView2 then you can use standard Bootstrap and HTML from a FWH app :)
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Otto
Posts: 6396
Joined: Fri Oct 07, 2005 7:07 pm
Has thanked: 8 times
Been thanked: 1 time
Contact:

Re: Bootstrap for FiveWin

Post by Otto »

Hello Carlos,
thank you.
I will try it out right away. I will also test a few more controls.
It is currently just a concept test.
Best regards,
Otto
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
Posts: 6396
Joined: Fri Oct 07, 2005 7:07 pm
Has thanked: 8 times
Been thanked: 1 time
Contact:

Re: Bootstrap for FiveWin

Post by Otto »

Dear Antonio,
Thank you. We have already switched some screens to WebView2. However, I'm still not sure if WebView2 is worth the effort or if it might be better to go straight for mod_harbour or a microservice with FiveWin.
At the moment, I’m leaning more towards that.

Best regards,
Otto
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
Post Reply