Files
EgtExecutor/LUA_GenDialog.cpp
T
Dario Sassi a14e14c641 EgtExecutor 3.1g2 :
- sistemazioni per selezione entità in dialogo lua.
2026-07-07 09:06:59 +02:00

781 lines
33 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//----------------------------------------------------------------------------
// EgalTech 2026-2026
//----------------------------------------------------------------------------
// File : LUA_GenDialog.cpp Data : 06.07.26 Versione : 3.1g2
// Contenuto : Funzioni gestione dialoghi per LUA.
//
//
//
// Modifiche : 06.07.26 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "DllMain.h"
#include "EXE.h"
#include "LUA.h"
#include "LUA_GenDialog.h"
#include "resource.h"
#include "/EgtDev/Include/ExeExecutor.h"
#include "/EgtDev/Include/EGkLuaAux.h"
#include "/EgtDev/Include/EGkStringUtils3d.h"
#include "/EgtDev/Include/EgtIniFile.h"
#include "/EgtDev/Include/EgtNumUtils.h"
#include <Windowsx.h>
#include <shlobj.h>
using namespace std ;
//----------------------------------------------------------------------------
// Costanti
const int MAX_CTRLS = IDC_TEXT8 - IDC_TEXT1 + 1 ;
const int OFFS_CTRLS = 40 ;
enum TYPE_CTRLS { CTRL_NONE = 0, CTRL_CHECK = 1, CTRL_COMBO = 2, CTRL_EDIT = 3, CTRL_COLOR = 4, CTRL_BUTTON = 5} ;
const char* SEC_SCENE = "Scene" ;
const char* KEY_CUSTOMCOLORS = "CustomColors" ;
// Tipi
struct DialogContext {
bool bDone = false ; // flag per chiusura dialogo (X/Ok/Cancel)
bool bCancelled = false ; // true se X/Cancel
STRVECTOR vsResult ; // valori finali da ritornare a Lua
} ;
// Variabili Globali
static COLORREF s_CustomColors[16] = {12632256} ; // 16 colori GRAY
static bool s_bCustomColorsInit = false ; // flag di inizializzazione colori
static HWND s_hDlg = NULL ;
static string s_sCaption ;
static int s_nCtrls = 0 ;
static string s_sText[MAX_CTRLS] ;
static string s_sEdit[MAX_CTRLS] ;
static int s_nType[MAX_CTRLS] ;
static bool s_bSelectionMode = false ; // se true allora diventa dialogo "Modeless"
static int s_nActiveRow = - 1 ; // indice della riga di "SB:" attiva
static int s_nDlgItem = -1 ; // indice dell'Item di selezione
//-------------------------------------------------------------------------------
// OnClosingDialog
//
// Scopo:
// Gestisce la chiusura del dialogo modeless in modo centralizzato e sicuro.
// Questa funzione viene chiamata quando lutente chiude il dialogo tramite:
// - OK
// - Cancel
// - pulsante X
// - WM_CLOSE
//
// Comportamento:
// - Riabilita la finestra principale (Main Window), che era stata disabilitata durante il funzionamento
// modale del dialogo.
// - Distrugge la finestra del dialogo.
// - Riporta il focus alla finestra principale, garantendo continuità nellinterazione dellutente.
// - Deseleziona tutti gli oggetti nella scena e aggiorna la vista, in modo che nessuna selezione residua
// rimanga attiva dopo la chiusura.
//-------------------------------------------------------------------------------
static void
OnClosingDialog( HWND hwndDlg)
{
// reset stato globale
s_bSelectionMode = false ;
s_nActiveRow = -1 ;
s_nDlgItem = -1 ;
// il controllo passa al Main
HWND hMain = GetParent( hwndDlg) ;
EnableWindow( hMain, TRUE) ;
DestroyWindow( hwndDlg) ;
s_hDlg = NULL ;
SetFocus( hMain) ;
// deseleziono tutto
ExeDeselectAll() ;
ExeDraw() ;
}
//-------------------------------------------------------------------------------
// StartSelectionMode
//
// Scopo:
// Attiva la modalità di selezione (modeless) del dialogo. Questa modalità permette allutente di interagire
// con la scena mentre il dialogo rimane aperto e attivo.
//
// Parametri:
// hDlg → handle del dialogo
// nEditId → ID delledit associato al pulsante BS premuto
// nActiveRow → indice della riga BS attiva (0..MAX_CTRLS-1)
//-------------------------------------------------------------------------------
static void
StartSelectionMode( HWND hDlg, int nEditId, int nActiveRow)
{
// il dialogo torna modeless rispetto alla scena
HWND hMain = GetParent( hDlg) ;
EnableWindow( hMain, TRUE) ;
// resta comunque in primo piano
SetWindowPos( hDlg, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) ;
// disabilito tutti gli edit associati agli SB tranne quello attivo
for ( int i = 0; i < s_nCtrls ; ++ i) {
if ( s_sEdit[i].find( "SB:") == 0) {
int idEdit = IDC_EDIT1 + i ;
EnableWindow( GetDlgItem( hDlg, idEdit), ( i == nActiveRow ? TRUE : FALSE)) ;
}
}
// seleziono solo gli elementi presenti nell'edit corrispondente
s_nDlgItem = - 1 ;
ExeDeselectAll() ; // !<-- NB. avendo messo s_nDlgItem = -1 il testo non viene rimosso
s_bSelectionMode = true ;
s_nActiveRow = nActiveRow ;
s_nDlgItem = nEditId ;
AtoWEX<128> wsEdit( "") ;
GetDlgItemText( hDlg, nEditId, LPWSTR( wsEdit), 128) ;
string sIds = wstrztoA( wsEdit) ;
STRVECTOR vsIds ;
Tokenize( sIds, ",", vsIds) ;
for ( const string& sId : vsIds) {
int nId = GDB_ID_NULL ;
if ( FromString( sId, nId) && nId != GDB_ID_NULL)
ExeSelectObj( nId) ;
}
ExeDraw() ;
}
//-------------------------------------------------------------------------------
// EndSelectionMode
//
// Scopo:
// Termina la modalità di selezione e riporta il dialogo in stato modale.
//-------------------------------------------------------------------------------
static void
EndSelectionMode( HWND hDlg)
{
s_bSelectionMode = false ;
s_nActiveRow = -1 ;
s_nDlgItem = -1 ;
// disabilito tutti gli edit associati ai BS
for ( int i = 0; i < s_nCtrls ; ++ i) {
if ( s_sEdit[i].find( "SB:") == 0) {
int idEdit = IDC_EDIT1 + i ;
EnableWindow( GetDlgItem( hDlg, idEdit), FALSE) ;
SetDlgItemText( hDlg, IDC_BUTTON_SEL1 + i, strztoW( "+")) ;
}
}
// il dialogo torna modale
HWND hMain = GetParent( hDlg) ;
EnableWindow( hMain, FALSE) ;
SetFocus( hDlg) ;
// deseleziono tutto
ExeDeselectAll() ; //!<-- NB. avendo messo nDlgModelessItem = -1 il testo non viene rimosso
ExeDraw() ;
}
//-----------------------------------------------------------------------------
LRESULT
CALLBACK UpdateDialogSelection( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
if ( msg == WM_GETDLGCODE)
return DLGC_WANTALLKEYS ;
if ( msg == WM_KEYDOWN && wParam == VK_RETURN) {
// dwRefData contiene l'ID dell'EDIT
int nEditId = int( dwRefData) ;
HWND hwndDlg = GetParent(hwnd) ;
if ( hwndDlg == nullptr)
return FALSE ;
// recupero il testo dell'EDIT corretto
string sIds ;
AtoWEX<128> wsEdit( "") ;
if ( GetDlgItemText( hwndDlg, nEditId, LPWSTR( wsEdit), 128) > 0)
sIds = wstrztoA( wsEdit) ;
// deseleziono tutto
ExeDeselectAll() ;
// seleziono gli ID contenuti
if ( ! sIds.empty()) {
STRVECTOR vsIds;
Tokenize( sIds, ",", vsIds) ;
for ( const string& s : vsIds) {
int nId = GDB_ID_NULL ;
if ( FromString( s, nId) && nId != GDB_ID_NULL)
ExeSelectObj( nId) ;
}
}
// aggiorno la grafica ed esco
ExeDraw() ;
return TRUE ;
}
return ( DefSubclassProc( hwnd, msg, wParam, lParam)) ;
}
//-------------------------------------------------------------------------------
BOOL
CALLBACK DialogBoxProc( HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
// variabili static locali per mantenere lo stato tra chiamate per selezione colori
static COLORREF selectedColor[MAX_CTRLS] = {0} ; // BLACK
static HBRUSH hColorBrush[MAX_CTRLS] = {NULL} ; // non definito
switch ( message) {
case WM_INITDIALOG :
{
// imposto il dialogo come modale
HWND hMain = GetParent( hwndDlg) ;
EnableWindow( hMain, FALSE) ;
SetFocus( hwndDlg) ;
// imposto il contesto della funzione per recuperalo in futuro (per la funzione di CallBack Lua)
SetWindowLongPtr( hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam) ;
// colore di default
COLORREF defaultColor = RGB( 255, 0, 0) ;
// inizializzazione colori e brush
for ( int i = 0 ; i < MAX_CTRLS ; ++ i) {
selectedColor[i] = defaultColor ;
if ( hColorBrush[i] != NULL)
DeleteObject( hColorBrush[i]) ;
hColorBrush[i] = CreateSolidBrush( selectedColor[i]) ;
}
// dati del dialogo
HWND hwndOwner = GetParent( hwndDlg) ;
if ( hwndOwner == nullptr)
hwndOwner = GetDesktopWindow() ;
RECT rcOwner, rcDlg ;
GetWindowRect( hwndOwner, &rcOwner) ;
GetWindowRect( hwndDlg, &rcDlg) ;
// determino la posizione dell'ultima riga di dati da visualizzare
HWND hwndLR = GetDlgItem( hwndDlg, IDC_TEXT1 + Clamp( s_nCtrls, 1, MAX_CTRLS) - 1) ;
RECT rcLR ;
GetWindowRect( hwndLR, &rcLR) ;
POINT ptLR{ rcLR.left, rcLR.top} ;
ScreenToClient( hwndDlg, &ptLR) ;
// centro e scalo il dialogo
int nNewH = rcLR.top - rcOwner.top + 2 * OFFS_CTRLS ; // non chiaro perchè va sottratto rcOwner.top ma funziona sempre
SetWindowPos( hwndDlg, HWND_TOP,
( rcOwner.left + rcOwner.right - ( rcDlg.right - rcDlg.left)) / 2,
( rcOwner.top + rcOwner.bottom - nNewH) / 2,
( rcDlg.right - rcDlg.left),
nNewH, 0) ;
// riposiziono il bottone Ok
HWND hwndOk = GetDlgItem( hwndDlg, IDOK) ;
RECT rcOk ;
GetWindowRect( hwndOk, &rcOk) ;
POINT ptOk{ rcOk.left, rcOk.top} ;
ScreenToClient( hwndDlg, &ptOk) ;
SetWindowPos( hwndOk, hwndDlg, ptOk.x, ptLR.y + OFFS_CTRLS, 0, 0, SWP_NOSIZE | SWP_NOZORDER) ;
// riposiziono il bottone Cancel
HWND hwndCl = GetDlgItem( hwndDlg, IDCANCEL) ;
RECT rcCl ;
GetWindowRect( hwndCl, &rcCl) ;
POINT ptCl{ rcCl.left, rcCl.top} ;
ScreenToClient( hwndDlg, &ptCl) ;
SetWindowPos( hwndCl, hwndDlg, ptCl.x, ptLR.y + OFFS_CTRLS, 0, 0, SWP_NOSIZE | SWP_NOZORDER) ;
// stile finestra
LONG style = GetWindowLong( hwndDlg, GWL_STYLE) ;
style |= WS_SYSMENU ;
SetWindowLong( hwndDlg, GWL_STYLE, style) ;
}
// assegno titolo del dialogo
SetWindowText( hwndDlg, stringtoW( s_sCaption)) ;
// assegno testi e valori di default e nascondo linee dei controlli non usati
for ( int i = 0 ; i < MAX_CTRLS ; ++ i) {
if ( i < s_nCtrls) {
// imposto il testo nella riga corrente
SetDlgItemText( hwndDlg, IDC_TEXT1 + i, stringtoW( s_sText[i])) ;
// --- se CheckBox
if ( s_sEdit[i].find( "CK:") == 0) {
bool bChecked = false ;
FromString( s_sEdit[i].substr( 3), bChecked) ;
CheckDlgButton( hwndDlg, IDC_CHECK1 + i, ( bChecked ? BST_CHECKED : BST_UNCHECKED)) ;
ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; // nascondo Edit
ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo Combo
ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione
s_nType[i] = CTRL_CHECK ;
}
// --- se ComboBox
else if ( s_sEdit[i].find( "CB:") == 0) {
string sList = s_sEdit[i].substr( 3) ;
STRVECTOR vsVal ;
Tokenize( sList, ",", vsVal) ;
int nSel = 0 ;
HWND hwndCBox = GetDlgItem( hwndDlg, IDC_COMBO1 + i) ;
for ( int j = 0 ; j < ssize( vsVal) ; ++ j) {
string sItem ;
if ( vsVal[j][0] == '*') {
nSel = j ;
sItem = vsVal[j].substr( 1) ;
}
else
sItem = vsVal[j] ;
ComboBox_AddString( hwndCBox, stringtoW( sItem)) ;
}
ComboBox_SetCurSel( hwndCBox, nSel) ;
int nEditHeight = int( SendMessage( hwndCBox, CB_GETITEMHEIGHT, (WPARAM)-1, 0)) ;
int nItemHeight = int( SendMessage( hwndCBox, CB_GETITEMHEIGHT, 0, 0)) ;
int nTotalHeight = nEditHeight + nItemHeight * ( ssize( vsVal) + 1) ;
RECT rc ; GetWindowRect( hwndCBox, &rc) ;
HWND parent = GetParent(hwndCBox) ; MapWindowPoints( NULL, parent, (LPPOINT)&rc, 2) ;
SetWindowPos( hwndCBox, NULL, rc.left, rc.top, rc.right - rc.left, nTotalHeight, SWP_NOZORDER) ;
ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; // nascondo Edit
ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo CheckBox
ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione
s_nType[i] = CTRL_COMBO ;
}
// --- se ColorPicker
else if ( s_sEdit[i].find( "CP:") == 0) {
string sVal = s_sEdit[i].substr( 3) ;
Color CColor ;
if ( ! FromString( sVal, CColor))
CColor = INVISIBLE ;
selectedColor[i] = RGB( CColor.GetIntRed(), CColor.GetIntGreen(), CColor.GetIntBlue()) ;
if ( hColorBrush[i] != NULL)
DeleteObject( hColorBrush[i]) ;
hColorBrush[i] = CreateSolidBrush( selectedColor[i]) ;
// recupero i valori dei colori personalizzati dal file .ini
if ( ! s_bCustomColorsInit) {
// recupero il file Ini
string sIniFile = ExeGetIniFile() ;
if ( ! sIniFile.empty()) {
string sCustomVals = GetPrivateProfileStringUtf8( SEC_SCENE, KEY_CUSTOMCOLORS, "", sIniFile.c_str()) ;
STRVECTOR vsWinColors ; Tokenize( sCustomVals, ",", vsWinColors) ;
for ( int j = 0 ; j < ssize( vsWinColors) && j < 16 ; ++ j) {
unsigned int unVal ;
if ( FromString( vsWinColors[j], unVal)) {
unsigned int unR = unVal & 0xFF ;
unsigned int unG = ( unVal >> 8) & 0xFF ;
unsigned int unB = ( unVal >> 16) & 0xFF ;
s_CustomColors[j] = RGB( unR, unG, unB) ;
}
}
}
s_bCustomColorsInit = true ;
}
// nascondo qualsiasi tipo di componente
ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ; // nascondo Edit
ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo Combo
ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo Check
ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione
// margini per dimensione del rettangolo preview pickColor
const int PREVIEW_HEIGHT = 20 ; // altezza del rettangolo
const int PREVIEW_MIN_WIDTH = 40 ; // larghezza minima
// creazione della static preview
HWND hwndPreview = NULL ;
HWND hwndEdit = GetDlgItem( hwndDlg, IDC_EDIT1 + i) ;
RECT rcEdit ; GetClientRect( hwndDlg, &rcEdit) ; // coordinate client del dialog
if ( hwndEdit != nullptr && GetWindowRect( hwndEdit, &rcEdit)) {
// converti rcLabel in coordinate client
POINT ptEdit = { rcEdit.left, rcEdit.top } ;
ScreenToClient( hwndDlg, &ptEdit) ;
int x = ptEdit.x ;
int y = ptEdit.y ;
int width = rcEdit.right - rcEdit.left ;
if ( width < PREVIEW_MIN_WIDTH)
width = PREVIEW_MIN_WIDTH ;
hwndPreview = CreateWindowEx( 0, L"STATIC", NULL,
WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_CENTERIMAGE | WS_BORDER,
x, y, width, PREVIEW_HEIGHT,
hwndDlg, ( HMENU)INT_PTR( IDC_COLOR1 + i),
GetModuleHandle( NULL), NULL) ;
}
// pulizia elementi testuali
if ( hwndPreview) {
SetWindowText( hwndPreview, L"") ;
SetWindowPos( hwndPreview, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) ;
InvalidateRect( hwndPreview, NULL, TRUE) ;
UpdateWindow( hwndPreview) ;
}
s_nType[i] = CTRL_COLOR ;
}
// --- se Button di selezione
else if ( s_sEdit[i].find( "SB:") == 0) {
ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo la Combo
ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo la Check
SetDlgItemText( hwndDlg, IDC_BUTTON_SEL1 + i, strztoW( "+")) ; // modalità non Selezione
EnableWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), FALSE) ; // disattivo la Edit
// associo alla casella di testo la funzione di KeyPress su Enter
HWND hEdit = GetDlgItem( hwndDlg, IDC_EDIT1 + i) ;
if ( hEdit != nullptr)
SetWindowSubclass( hEdit, UpdateDialogSelection, 1, (DWORD_PTR)( IDC_EDIT1 + i)) ;
s_nType[i] = CTRL_BUTTON ;
}
// --- se Testo
else {
SetDlgItemText( hwndDlg, IDC_EDIT1 + i, stringtoW( s_sEdit[i])) ;
ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ; // nascondo Combo
ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ; // nascondo Check
ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ; // nascondo Button di selezione
s_nType[i] = CTRL_EDIT ;
}
}
// se numero di controllo superiore al massimo, non visualizzo nullo ( in questo caso aggiungere elementi a Dialog in .rc)
else {
// nascondo tutto
ShowWindow( GetDlgItem( hwndDlg, IDC_TEXT1 + i), SW_HIDE) ;
ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT1 + i), SW_HIDE) ;
ShowWindow( GetDlgItem( hwndDlg, IDC_COMBO1 + i), SW_HIDE) ;
ShowWindow( GetDlgItem( hwndDlg, IDC_CHECK1 + i), SW_HIDE) ;
ShowWindow( GetDlgItem( hwndDlg, IDC_BUTTON_SEL1 + i), SW_HIDE) ;
s_nType[i] = CTRL_NONE ;
}
}
break ;
case WM_CTLCOLORSTATIC :
{
HDC hdcStatic = ( HDC)wParam ;
HWND hwndCtrl = ( HWND)lParam ;
int ctrlId = GetDlgCtrlID( hwndCtrl) ;
if ( ctrlId >= IDC_COLOR1 && ctrlId <= IDC_COLOR8) {
int idx = ctrlId - IDC_COLOR1 ;
if ( hColorBrush[idx] == NULL)
hColorBrush[idx] = CreateSolidBrush( selectedColor[idx]) ;
SetBkMode( hdcStatic, OPAQUE) ;
SetBkColor( hdcStatic, selectedColor[idx]) ;
return ( INT_PTR)hColorBrush[idx] ;
}
}
break ;
case WM_COMMAND :
{
int id = LOWORD( wParam) ;
int code = HIWORD( wParam) ;
// --- se click di un Button di selezione
if ( id >= IDC_BUTTON_SEL1 && id < IDC_BUTTON_SEL1 + MAX_CTRLS) {
int nBtnId = id ;
int nEditId = IDC_EDIT1 + ( id - IDC_BUTTON_SEL1) ;
int nActiveRow = nEditId - IDC_EDIT1 ;
// se il bottone premuto è lo stesso
if ( s_nDlgItem == nEditId) {
// se selezione attiva, allora deve terminare
if ( s_bSelectionMode) {
SetDlgItemText( hwndDlg, nBtnId, strztoW( "+")) ;
EndSelectionMode( hwndDlg) ;
}
// se selezione non attiva, allora viene attivata
else {
SetDlgItemText( hwndDlg, nBtnId, strztoW( "S")) ;
StartSelectionMode( hwndDlg, nEditId, nActiveRow) ;
}
}
// se nuovo buttone premuto
else {
// se esisteva una selezione precedente, questa viene annullata
if ( s_nDlgItem != -1) {
// recupero il Button di selezione precedente e modifico il suo testo
int nPrevBtnId = IDC_BUTTON_SEL1 + ( s_nDlgItem - IDC_EDIT1) ;
SetDlgItemText( hwndDlg, nPrevBtnId, strztoW( "+")) ;
}
// la selezione passa al comando corrente
s_nDlgItem = nEditId ;
SetDlgItemText( hwndDlg, nBtnId, strztoW( "S")) ;
// il dialogo diventa "Modeless" temporaneamente
s_nActiveRow = nEditId - IDC_EDIT1 ;
StartSelectionMode( hwndDlg, nEditId, nActiveRow) ;
}
return TRUE ; // !<-- esco
}
// --- se comando di Preview per brush di colori mediante click del mouse
else if ( code == STN_CLICKED && id >= IDC_COLOR1 && id <= IDC_COLOR8) {
// se selezione attiva, allora deve terminare
if ( s_bSelectionMode) {
// se esisteva una selezione precedente, questa viene annullata
if ( s_nDlgItem != -1) {
// recupero il Button di selezione precedente e modifico il suo testo
int nPrevBtnId = IDC_BUTTON_SEL1 + ( s_nDlgItem - IDC_EDIT1) ;
SetDlgItemText( hwndDlg, nPrevBtnId, strztoW( "+")) ;
}
EndSelectionMode( hwndDlg) ;
}
int idx = id - IDC_COLOR1 ;
HWND hwndPreview = GetDlgItem( hwndDlg, IDC_COLOR1 + idx) ;
if ( ! hwndPreview)
return TRUE ;
// apertura ChooseColor (usa hwndDlg come owner)
CHOOSECOLOR cc = {} ;
cc.lStructSize = sizeof( cc) ;
cc.hwndOwner = hwndDlg ;
cc.lpCustColors = s_CustomColors ;
cc.rgbResult = selectedColor[idx] ;
cc.Flags = CC_FULLOPEN | CC_RGBINIT ;
if ( ChooseColor( &cc)) {
selectedColor[idx] = cc.rgbResult ;
// salvo nel file Ini i nuovi colori personalizzati
string sIniFile = ExeGetIniFile() ;
if ( ! sIniFile.empty()) {
string sOut = "" ;
for ( int j = 0 ; j < ssize( s_CustomColors) && j < 16 ; ++ j)
sOut += ToString( ( unsigned int)s_CustomColors[j]) + "," ;
sOut.pop_back() ;
WritePrivateProfileStringUtf8( SEC_SCENE, KEY_CUSTOMCOLORS, sOut.c_str(), sIniFile.c_str()) ;
}
if ( hColorBrush[idx] != NULL)
DeleteObject( hColorBrush[idx]) ;
hColorBrush[idx] = CreateSolidBrush( selectedColor[idx]) ;
InvalidateRect( hwndPreview, NULL, TRUE) ;
UpdateWindow( hwndPreview) ;
}
return TRUE ; // !<-- Esco
}
switch ( LOWORD( wParam)) {
case IDOK :
{
// recupero il contesto impostato alla creazione della finestra (durante esecuzione WM_INITDIALOG)
DialogContext* pdcCtx = (DialogContext*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA) ;
if ( pdcCtx != nullptr) {
// inizializzo i risultati
pdcCtx->vsResult.clear() ;
pdcCtx->vsResult.reserve( s_nCtrls) ;
// recupero i valori degli Item
for ( int i = 0 ; i < s_nCtrls ; ++ i) {
AtoWEX<128> wsEdit( "") ;
s_sEdit[i] = "" ;
switch ( s_nType[i]) {
case CTRL_CHECK :
s_sEdit[i] = ( IsDlgButtonChecked( hwndDlg, IDC_CHECK1 + i) == BST_CHECKED ? "1" : "0") ;
break ;
case CTRL_COMBO :
if ( GetDlgItemText( hwndDlg, IDC_COMBO1 + i, LPWSTR( wsEdit), 128) > 0)
s_sEdit[i] = wstrztoA( wsEdit) ;
break ;
case CTRL_EDIT :
if ( GetDlgItemText( hwndDlg, IDC_EDIT1 + i, LPWSTR( wsEdit), 128) > 0)
s_sEdit[i] = wstrztoA( wsEdit) ;
break ;
case CTRL_COLOR :
{
COLORREF col = selectedColor[i] ;
BYTE r = GetRValue( col), g = GetGValue( col), b = GetBValue( col) ;
char buf[16] ;
sprintf_s( buf, sizeof( buf), "%u,%u,%u", r, g, b) ;
s_sEdit[i] = buf ;
}
break ;
case CTRL_BUTTON :
if ( GetDlgItemText( hwndDlg, IDC_EDIT1 + i, LPWSTR( wsEdit), 128) > 0)
s_sEdit[i] = wstrztoA( wsEdit) ;
break ;
}
// inserisco i valori all'interno della tabella Lua
pdcCtx->vsResult.push_back( s_sEdit[i]) ;
}
pdcCtx->bCancelled = false ;
pdcCtx->bDone = true ;
}
// chiudo il dialogo
OnClosingDialog( hwndDlg) ;
return TRUE ;
}
[[fallthrough]] ;
case IDCANCEL :
{
// pulizia brush colori
for ( int i = 0 ; i < MAX_CTRLS ; ++ i) {
if ( hColorBrush[i] != NULL) {
DeleteObject( hColorBrush[i]) ;
hColorBrush[i] = NULL ;
}
}
// recupero il contesto impostato alla creazione della finestra ( durante esecuzione WM_INITDIALOG) e se esiste lo rimuovo
DialogContext* pdcCtx = ( DialogContext*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA) ;
if ( pdcCtx != nullptr) {
pdcCtx->bCancelled = true ;
pdcCtx->bDone = true ;
}
// chiudo il dialogo
OnClosingDialog( hwndDlg) ;
return TRUE ;
}
}
// se siamo in modalità di Selezione e il comando attivo non si riferisce ad un button di Selezione (SB), il dialogo torna modale
if ( s_bSelectionMode) {
bool bIsBS = ( id >= IDC_BUTTON_SEL1 && id < IDC_BUTTON_SEL1 + MAX_CTRLS) ;
bool bIsActiveEdit = ( id == IDC_EDIT1 + s_nActiveRow) ;
// il dialogo torna modale
if ( ! bIsBS && ! bIsActiveEdit)
EndSelectionMode( hwndDlg) ;
}
break ;
}
case WM_CLOSE :
{
// recupero il contesto impostato alla creazione della finestra ( durante esecuzione WM_INITDIALOG) e se esiste lo rimuovo
DialogContext* pdcCtx = (DialogContext*)GetWindowLongPtr( hwndDlg, GWLP_USERDATA) ;
if ( pdcCtx != nullptr) {
pdcCtx->bCancelled = true ;
pdcCtx->bDone = true ;
}
// chiudo il dialogo
OnClosingDialog( hwndDlg) ;
return TRUE ;
}
}
return FALSE ;
}
//-----------------------------------------------------------------------------
bool
UpdateDialogIds( void)
{
// verifico presenza dialogo e item di selezione
if ( s_hDlg == NULL || s_nDlgItem == -1)
return false ;
// recupero tutti gli elementi selezionati
string sIds ;
int nId = ExeGetFirstSelectedObj() ;
while ( nId != GDB_ID_NULL) {
sIds.append( ToString( nId) + ",") ;
nId = ExeGetNextSelectedObj() ;
}
if ( ! sIds.empty())
sIds.pop_back() ;
// aggiorno il contenuto nel dialogo
return ( SetDlgItemText( s_hDlg, s_nDlgItem, stringtoW( sIds))) ;
}
//-------------------------------------------------------------------------------
// LuaDialogBox (Modeless)
//
// Scopo:
// ------
// Questa funzione implementa un dialogo "modale" dal punto di vista del codice Lua, ma "modeless" dal punto di vista della
// UI Windows quando necessario. Lobiettivo è permettere al codice Lua di rimanere sospeso finché lutente non chiude il
// dialogo (OK/Cancel/X), mantenendo però la UI attiva e permettendo selezioni nella scena quando lutente preme un pulsante
// SB (Select Button).
//
// Perché serve:
// -------------
// - I dialoghi modali standard (DialogBox) bloccano completamente la UI, mentre i dialoghi modeless standard (CreateDialog)
// NON bloccano il codice Lua.
// - Le coroutine Lua (lua_yield/lua_resume) non sono utilizzabili in questo contesto perché il thread principale
// dellapplicazione non può essere sospeso tramite yield: il codice Lua non riprende correttamente e la UI non si
// aggiorna coerentemente)
//
// Come funziona:
// --------------
// 1) Il dialogo viene creato con CreateDialogParam(), quindi la UI rimane attiva.
// 2) La funzione NON ritorna subito: entra in un message loop locale (Esattamente equivalente al loop all'interno di
// DialogBox()). Il cuore del loop è GetMessage(), che è una chiamata BLOCCANTE. Significa che il thread rimane in
// attesa senza consumare CPU finché non arriva un messaggio. Non c’è polling, non c’è busy loop. Il comportamento è
// identico a quello di un dialogo modale nativo.
// 3) Il ciclo rimane attivo finché:
// - lutente non chiude il dialogo (OK/Cancel/X)
// - oppure la finestra viene distrutta
// In quel momento ctx->bDone viene impostato a true.
// 4) Durante la vita del dialogo, se lutente preme un pulsante SB, il dialogo diventa temporaneamente "modeless":
// - la main window viene abilitata
// - il dialogo rimane abilitato ma passa in secondo piano logico
// - lutente può selezionare oggetti nella scena
// Quando la selezione termina, il dialogo torna modale.
// 5) Quando il dialogo chiude, i valori vengono copiati in ctx->vsResult e ritornati a Lua come risultato della funzione.
//
// Vantaggi della soluzione:
// -------------------------
// - Lua rimane bloccato come con un dialogo modale tradizionale.
// - La UI rimane completamente attiva.
// - Le selezioni SB (mediante Select Button) funzionano senza bloccare il programma.
// - Nessun uso di coroutine Lua (che non erano affidabili in questo contesto).
// - Nessun consumo CPU anomalo.
// - Comportamento stabile e prevedibile.
//
// Nota importante:
// ----------------
// Il dialogo NON viene mai disabilitato durante la selezione SB (Select Button). Solo la main window viene
// abilitata temporaneamente.
// Questo è fondamentale: disabilitare il dialogo impedirebbe allutente di poterlo riselezionare dopo la selezione nella scena.
//
//-------------------------------------------------------------------------------
int
LuaDialogBox( lua_State* L)
{
// 1 .. 9 parametri : sCaption, { sText, sDefault}, ...
LuaCheckParam( L, 1, s_sCaption)
s_nCtrls = 0 ;
for ( int i = 0 ; i < MAX_CTRLS ; ++ i) {
STRVECTOR vData ;
if ( LuaGetParam( L, 2 + i, vData) && ssize( vData) >= 2) {
s_sText[i] = Trim( vData[0]) ;
s_sEdit[i] = Trim( vData[1]) ;
++ s_nCtrls ;
}
else
break ;
}
LuaClearStack( L) ;
// se UI disabilitata, esco
if ( ! ExeGetEnableUI()) {
LuaSetParam( L) ;
return 1 ;
}
// se dialogo già aperto, errore
if ( s_hDlg != NULL) {
LuaSetParam( L) ;
return 1 ;
}
// definizione del contesto
DialogContext dcCtx ;
// creazione di un dialogo Modeless
HWND hMainWnd = ExeGetMainWindowHandle() ;
s_nDlgItem = -1 ;
s_hDlg = ( CreateDialogParam( GetModuleIstance(), MAKEINTRESOURCE( IDD_LUADLG), hMainWnd,
(DLGPROC)DialogBoxProc, (LPARAM) &dcCtx)) ;
if ( s_hDlg == NULL) {
LuaSetParam( L) ;
return 1 ;
}
// porto la finestra in primo piano
ShowWindow( s_hDlg, SW_SHOW) ;
SetForegroundWindow( s_hDlg) ;
// contesto corrente
RECT rctWnd{ 0, 0, 0, 0} ;
GseContext* pGseCtx = GetCurrGseContext() ;
if ( pGseCtx != nullptr)
GetWindowRect( pGseCtx->m_hWnd, &rctWnd) ;
// message Loop
MSG msg ;
while ( ! dcCtx.bDone && IsWindow( s_hDlg)) {
if ( GetMessage( &msg, NULL, 0, 0) <= 0)
break ;
if ( ! IsDialogMessage( s_hDlg, &msg) &&
( ! s_bSelectionMode || PtInRect( &rctWnd, msg.pt) != FALSE)) {
TranslateMessage( &msg) ;
DispatchMessage( &msg) ;
}
}
// ritorno i valori lua
if ( dcCtx.bCancelled)
LuaSetParam( L) ;
else
LuaSetParam( L, dcCtx.vsResult) ;
return 1 ;
}