gui - wip

This commit is contained in:
Frater 2026-01-31 17:07:51 +01:00
commit 88debea9f9
5 changed files with 225 additions and 199 deletions

View file

@ -22,7 +22,7 @@ The project is intentionally educational, experimental, and minimalist.
- No reuse of existing BIOS code - No reuse of existing BIOS code
- Minimal but functional implementation - Minimal but functional implementation
- Compatible with QEMU - Compatible with QEMU
- Target architecture: **8086 → 80486** - Target architecture: **i386+**
- **Boot directly from ROM** - **Boot directly from ROM**
- No DOS, no bootloader, no disk dependency - No DOS, no bootloader, no disk dependency
@ -145,7 +145,7 @@ Le projet est volontairement **éducatif, expérimental et minimaliste**.
- Aucun code BIOS existant réutilisé - Aucun code BIOS existant réutilisé
- Implémentation minimale mais fonctionnelle - Implémentation minimale mais fonctionnelle
- Compatible avec QEMU - Compatible avec QEMU
- Architecture ciblée : **8086 jusqu’au 486** - Architecture ciblée : **i386+**
- **Démarrer directement depuis la ROM** - **Démarrer directement depuis la ROM**
- Sans DOS, sans bootloader, sans disque - Sans DOS, sans bootloader, sans disque

View file

@ -100,63 +100,68 @@ reset:
push cs push cs
pop ds pop ds
setup_gui: ; Init du système GUI
; Initialiser le segment de données correct pour le texte du bouton call gui_init_system
mov ax, cs ; ou DS selon où sont tes chaines
mov [btn_quit + widget.text_seg], ax
; GUI WINDOW, 50,50,250,100,helloworld ; GFX RECTANGLE_FILL, 50, 50, 100, 30, 1
; GFX RECTANGLE_DRAW, 50,50,250,100,0
;GFX LINE_VERT, 50, 50, 100, 0
;GFX LINE_VERT, 300, 50, 100, 0
;GFX LINE_HORIZ, 50, 300, 50, 0
;GFX LINE_HORIZ, 50, 300, 100, 0
call build_interface
mov ax,BDA_DATA_SEG main_loop:
mov ds,ax ; 2. Moteur GUI (Gère tout : Loop, HitTest, Draw, Callbacks)
endless: ; DS pointe déjà sur GUI_RAM_SEG
call gui_process_all
; 1. Récupérer souris ; 3. Afficher souris
mov ax, [BDA_MOUSE + mouse.x] ; GFX MOUSE_MOVE
mov cx, ax ; CX = Souris X
mov ax, [BDA_MOUSE + mouse.y]
mov dx, ax ; DX = Souris Y
mov bl, [BDA_MOUSE + mouse.status] ; BL = Boutons
; 2. Gérer le bouton jmp main_loop
mov si, btn_quit ; SI pointe sur l'objet
call gui_update_button ; Met à jour l'état et redessine si besoin
; 3. Vérifier si cliqué (AL = 1) ; --- Callbacks (Fonctions appelées par le moteur) ---
cmp al, 1 on_click_quit:
je action_quitter hlt
jmp on_click_quit
; 4. Afficher le curseur souris (Important : après avoir dessiné le bouton)
; GFX MOUSE_MOVE
GFX TXT_MODE, GFX_TXT_BLACK build_interface:
GFX GOTOXY, 0, 180 ; --- CRÉATION DYNAMIQUE DES BOUTONS ---
; On reste dans CS pour lire les arguments,
; mais on configure DS=GUI_RAM_SEG pour l'allocation
mov ax, [BDA_MOUSE + mouse.x] ; Créer Bouton 1 "QUITTER"
call print_word_hex call gui_alloc_widget ; Retourne SI = Pointeur nouveau slot
jc .mem_full
GFX PUTCH, ' ' ; Remplir les propriétés
mov word [gs:si + widget.x], 10
mov word [gs:si + widget.y], 10
mov word [gs:si + widget.w], 80
mov word [gs:si + widget.h], 20
mov word [gs:si + widget.text_ofs], str_quit
mov word [gs:si + widget.text_seg], cs ; Texte est dans la ROM
mov word [gs:si + widget.event_click], on_click_quit ; Fonction à appeler
mov ax, [BDA_MOUSE + mouse.y] ; Créer Bouton 2 "HELLO"
call print_word_hex call gui_alloc_widget
jc .mem_full
jmp endless mov word [gs:si + widget.x], 100
mov word [gs:si + widget.y], 10
mov word [gs:si + widget.w], 80
mov word [gs:si + widget.h], 20
mov word [gs:si + widget.text_ofs], str_hello
mov word [gs:si + widget.text_seg], cs
; Pas de callback
action_quitter: .mem_full:
hlt ret
jmp action_quitter
; --- Données ROM ---
str_quit db "QUITTER", 0
str_hello db "HELLO", 0
; ----------------------------------------------------------- ; -----------------------------------------------------------
; timer ISR (IRQ -> INT) ; timer ISR (IRQ -> INT)
; ----------------------------------------------------------- ; -----------------------------------------------------------
@ -175,21 +180,6 @@ timer_isr:
pop ax pop ax
iret iret
; Déclaration d'un bouton "Quitter"
btn_quit:
istruc widget
at widget.x, dw 10
at widget.y, dw 10
at widget.w, dw 80
at widget.h, dw 20
at widget.state, db GUI_STATE_NORMAL
at widget.text_ofs, dw str_quit
at widget.text_seg, dw 0 ; Sera rempli au runtime (CS ou DS)
at widget.id, db 1
iend
str_quit db "QUITTER", 0
; ------------------------------------------------------------------ ; ------------------------------------------------------------------
; Padding jusqu'au reset vector ; Padding jusqu'au reset vector
; ------------------------------------------------------------------ ; ------------------------------------------------------------------

View file

@ -837,13 +837,13 @@ cga_fill_rect:
pusha pusha
push es push es
; Setup ES
mov ax, VIDEO_SEG mov ax, VIDEO_SEG
mov es, ax mov es, ax
call cga_mouse_hide call cga_mouse_hide
; 1. Tri des coordonnées ; --- 1. Tri des coordonnées (X et Y) ---
; Tri X
mov ax, .x1 mov ax, .x1
mov bx, .x2 mov bx, .x2
cmp ax, bx cmp ax, bx
@ -853,6 +853,7 @@ cga_fill_rect:
mov .x2, bx mov .x2, bx
.x_sorted: .x_sorted:
; Tri Y
mov ax, .y1 mov ax, .y1
mov bx, .y2 mov bx, .y2
cmp ax, bx cmp ax, bx
@ -862,35 +863,35 @@ cga_fill_rect:
mov .y2, bx mov .y2, bx
.y_sorted: .y_sorted:
; Calculer hauteur ; --- 2. Calcul de la hauteur ---
sub bx, ax ; BX = y2 - y1 mov bx, .y2
inc bx ; BX = hauteur sub bx, .y1
mov dx, bx ; DX = Compteur de hauteur inc bx ; BX = Hauteur finale
push bx ; SAUVEGARDER LA HAUTEUR SUR LA PILE [STACK A]
; (Car on va utiliser BX dans cga_calc_addr ou juste après)
; 2. Calculer l'adresse de départ (Haut-Gauche) ; --- 3. Calcul adresse de départ ---
mov cx, .x1 ; CX = x1 mov cx, .x1
mov dx, .y1 ; DX = y1 mov dx, .y1
push dx ; Sauve Y1 (car cga_calc_addr peut modifier DX/BX selon implémentation)
; Note: cga_calc_addr attend CX=x, DX=y ; Note: cga_calc_addr attend CX=x, DX=y et retourne DI
; Si cga_calc_addr modifie BX, la sauvegarde [STACK A] est vitale.
call cga_calc_addr ; DI = Offset début ligne call cga_calc_addr ; DI = Offset début ligne
pop bx ; Récupère Y1 dans BX (pour savoir si pair/impair) ; --- 4. Préparer les masques ---
; Masque Gauche (DH)
; 3. Préparer les masques gauche/droite (comme Line Horizontal)
; Masque Gauche
mov cx, .x1 mov cx, .x1
and cx, 7 and cx, 7
mov dh, 0xFF mov dh, 0xFF
shr dh, cl ; DH = Masque Gauche shr dh, cl
; Masque Droite ; Masque Droite (AH)
mov cx, .x2 mov cx, .x2
and cx, 7 and cx, 7
inc cx inc cx
mov ah, 0xFF mov ah, 0xFF
shr ah, cl shr ah, cl
not ah ; AH = Masque Droite not ah
; Index octets ; Index octets
mov si, .x1 mov si, .x1
@ -898,35 +899,32 @@ cga_fill_rect:
mov cx, .x2 mov cx, .x2
shr cx, 3 ; CX = Index end byte shr cx, 3 ; CX = Index end byte
; Récupérer la hauteur (Y2 - Y1 + 1) ; --- 5. Récupérer la hauteur dans un registre sûr ---
mov bp, .y2 pop bx ; RESTAURER HAUTEUR DANS BX [STACK A]
sub bp, .y1 ; BX est maintenant notre compteur de boucle.
inc bp ; BP = Hauteur (Loop counter) ; BP reste intact, donc .color est accessible !
; Sauvegarder l'état pair/impair initial
; Si Y1 est impair, le premier saut de ligne doit être "vers le haut" (-0x2000 + 80)
; ou plus simplement : on gère le flip 0x2000 à chaque ligne.
; --- BOUCLE VERTICALE --- ; --- BOUCLE VERTICALE ---
.row_loop: .row_loop:
push di ; Sauve le début de la ligne actuelle push di ; Sauve début ligne
push si ; Sauve index byte start push si ; Sauve index byte
; --- Remplissage Horizontal (copié de line_horizontal) --- ; --- Remplissage Horizontal ---
; Cas Single Byte ; Cas Single Byte (Start == End)
cmp si, cx cmp si, cx
je .single_byte_row je .single_byte_row
; Partie Gauche ; Partie Gauche
mov al, dh ; Masque Gauche mov al, dh
call .apply_mask_fill call .apply_mask_fill
inc di inc di
inc si inc si
; Partie Centrale ; Partie Centrale (Boucle tant que SI < CX)
jmp .check_mid jmp .check_mid
.mid_loop: .mid_loop:
; Ici BP est valide, donc .color est correct
cmp byte .color, 0 cmp byte .color, 0
je .blk je .blk
mov byte [es:di], 0xFF mov byte [es:di], 0xFF
@ -941,42 +939,36 @@ cga_fill_rect:
jl .mid_loop jl .mid_loop
; Partie Droite ; Partie Droite
mov al, ah ; Masque Droite mov al, ah
call .apply_mask_fill call .apply_mask_fill
jmp .row_done jmp .row_done
.single_byte_row: .single_byte_row:
mov al, dh mov al, dh
and al, ah ; Intersection masques and al, ah ; Intersection des masques
call .apply_mask_fill call .apply_mask_fill
.row_done: .row_done:
pop si pop si
pop di ; Restaure début de ligne pop di
; --- Passage à la ligne suivante (Logique CGA optimisée) --- ; --- Passage ligne suivante (CGA Bank Switch) ---
xor di, 0x2000 ; Flip banque xor di, 0x2000
test di, 0x2000 test di, 0x2000
jnz .bank_switch_ok ; Si on passe de 0->1, on a descendu d'une ligne visuelle jnz .bank_switch_ok
add di, 80 ; Si on passe de 1->0, on remonte, donc faut ajouter 80 add di, 80
.bank_switch_ok: .bank_switch_ok:
dec bp dec bx ; On décrémente BX (Hauteur) au lieu de BP
jnz .row_loop jnz .row_loop
call cga_mouse_show call cga_mouse_show
pop es pop es
; clean defs
%undef .x1
%undef .y1
%undef .x2
popa popa
leave leave
ret ret
; Helper local pour Fill Rect
; AL = Masque, .color = couleur
.apply_mask_fill: .apply_mask_fill:
cmp byte .color, 0 cmp byte .color, 0
jz .ap_black jz .ap_black
@ -986,6 +978,14 @@ cga_fill_rect:
not al not al
and byte [es:di], al and byte [es:di], al
ret ret
; Nettoyage des defines
%undef .x1
%undef .y1
%undef .x2
%undef .y2
%undef .color
; ;
; ------------------------------------------------------------ ; ------------------------------------------------------------
; GESTION DU CURSEUR ; GESTION DU CURSEUR

View file

@ -306,14 +306,15 @@ isr_mouse_handler:
; debut du décodage des données ; debut du décodage des données
mov bl, [BDA_MOUSE + mouse.buffer] ; status mov bl, [BDA_MOUSE + mouse.buffer] ; status
mov al,bl
; --- BOUTONS (Optionnel mais recommandé) --- ; --- BOUTONS ---
mov al,bl ; extraction des boutons
and al, 00000111b ; Bits 0, 1, 2 = Gauche, Droite, Milieu and al, 00000111b ; Bits 0, 1, 2 = Gauche, Droite, Milieu
mov [BDA_MOUSE + mouse.status], al mov [BDA_MOUSE + mouse.status], al
xor ax,ax xor ax,ax
mov al, byte [BDA_MOUSE + mouse.buffer+1] ; delta X mov al, byte [BDA_MOUSE + mouse.buffer+1] ; delta X
test bl,00010000b ; signe X test bl,00010000b ; signe X (4eme bit)
jz .x_pos jz .x_pos
mov ah,0xff ; X est négatif mov ah,0xff ; X est négatif
.x_pos: .x_pos:
@ -321,7 +322,7 @@ isr_mouse_handler:
xor ax,ax xor ax,ax
mov al, byte [BDA_MOUSE + mouse.buffer+2] ; delta Y mov al, byte [BDA_MOUSE + mouse.buffer+2] ; delta Y
test bl,00100000b ; signe Y test bl,00100000b ; signe Y (5eme bit)
jz .y_pos jz .y_pos
mov ah,0xff ; y est négatif mov ah,0xff ; y est négatif
.y_pos: .y_pos:

View file

@ -21,7 +21,7 @@
; ============================================================================= ; =============================================================================
; --- Configuration Mémoire --- ; --- Configuration Mémoire ---
%define GUI_RAM_SEG 0x0800 ; Segment de données UI %define GUI_RAM_SEG 0x0A00 ; Segment de données UI
%define GUI_MAX_WIDGETS 32 ; Nombre max de widgets simultanés %define GUI_MAX_WIDGETS 32 ; Nombre max de widgets simultanés
; --- Drapeaux & États --- ; --- Drapeaux & États ---
@ -39,7 +39,6 @@ struc widget
.y resw 1 ; Position Y .y resw 1 ; Position Y
.w resw 1 ; Largeur .w resw 1 ; Largeur
.h resw 1 ; Hauteur .h resw 1 ; Hauteur
.state resb 1 ; État (0=Normal, 1=Hover, 2=Pressed, 3=Disabled)
.text_ofs resw 1 ; Offset du texte .text_ofs resw 1 ; Offset du texte
.text_seg resw 1 ; Segment du texte .text_seg resw 1 ; Segment du texte
.event_click resw 1 ; adresse de la fonction "on click" .event_click resw 1 ; adresse de la fonction "on click"
@ -60,42 +59,46 @@ endstruc
; Entrée : DS doit pointer vers GUI_RAM_SEG ; Entrée : DS doit pointer vers GUI_RAM_SEG
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
gui_init_system: gui_init_system:
pushad push eax
push ds push cx
push es
push di
mov ax, GUI_RAM_SEG mov ax, GUI_RAM_SEG
mov ds, ax mov es, ax
mov di, 0 ; On commence au début du segment 0x0800 xor di, di
mov cx, (widget_size * GUI_MAX_WIDGETS) mov cx, (widget_size * GUI_MAX_WIDGETS)
shl cx, 2 ; cx / 4 shl cx, 2 ; cx / 4
xor eax, eax xor eax, eax
rep stosd ; Remplit tout de 0 rep stosd ; Remplit tout de 0
pop ds pop di
popad pop es
pop cx
pop eax
ret ret
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
; gui_alloc_widget ; gui_alloc_widget
; Cherche un slot vide et retourne son adresse ; Cherche un slot vide et retourne son adresse
; Sortie : ; Sortie :
; - Succès : CF=0 (Carry Clear), SI = Offset du widget ; - Succès : CF=0 (Carry Clear), DS:SI = Offset du widget
; - Echec : CF=1 (Carry Set), SI = Indéfini (ou 0) ; - Echec : CF=1 (Carry Set), DS:SI = Indéfini (ou 0)
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
gui_alloc_widget: gui_alloc_widget:
push ax
push cx push cx
push bx push bx
push ds
mov ax, GUI_RAM_SEG mov ax, GUI_RAM_SEG
mov ds, ax mov gs, ax
mov si, 0 ; Début du pool mov si, 0 ; Début du pool
mov cx, GUI_MAX_WIDGETS mov cx, GUI_MAX_WIDGETS
.loop_find: .loop_find:
cmp byte [si + widget.state], GUI_STATE_FREE cmp byte [gs:si + widget.state], GUI_STATE_FREE
je .found ; Si state == 0, c'est libre ! je .found ; Si state == 0, c'est libre !
add si, widget_size ; Sinon on passe au suivant add si, widget_size ; Sinon on passe au suivant
@ -107,16 +110,16 @@ gui_alloc_widget:
.found: .found:
; On initialise le slot trouvé ; On initialise le slot trouvé
mov byte [si + widget.state], GUI_STATE_NORMAL ; Marquer comme occupé mov byte [gs:si + widget.state], GUI_STATE_NORMAL ; Marquer comme occupé
; Reset des champs critiques pour éviter les déchets ; Reset des champs critiques pour éviter les déchets
mov word [si + widget.callback], 0 mov word [gs:si + widget.event_click], 0
clc ; Clear Carry Flag clc ; Clear Carry Flag
.done: .done:
pop ds
pop bx pop bx
pop cx pop cx
pop ax
ret ret
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
@ -125,11 +128,25 @@ gui_alloc_widget:
; Entrée : SI = Pointeur widget ; Entrée : SI = Pointeur widget
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
gui_free_widget: gui_free_widget:
mov byte [si + widget.state], GUI_STATE_FREE push eax
push es
mov ax, GUI_RAM_SEG
mov es, ax
xor di, di
mov cx, (widget_size)
shl cx, 2 ; cx / 4
xor eax, eax
rep stosd ; Remplit tout de 0
mov byte [gs:si + widget.state], GUI_STATE_FREE
; On efface visuellement le widget (optionnel, remplit de blanc) ; On efface visuellement le widget (optionnel, remplit de blanc)
; Pour l'instant on le marque juste libre, il sera redessiné par-dessus. ; Pour l'instant on le marque juste libre, il sera redessiné par-dessus.
ret
pop es
pop eax
ret
; ============================================================================= ; =============================================================================
; SECTION : MOTEUR D'ÉVÉNEMENTS (EVENT LOOP) ; SECTION : MOTEUR D'ÉVÉNEMENTS (EVENT LOOP)
@ -146,31 +163,46 @@ gui_free_widget:
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
gui_process_all: gui_process_all:
pusha pusha
push gs
mov ax, BDA_DATA_SEG
mov ds, ax
mov cx, [BDA_MOUSE + mouse.x]
mov dx, [BDA_MOUSE + mouse.y]
mov bl, [BDA_MOUSE + mouse.status]
mov ax, GUI_RAM_SEG
mov gs, ax
mov si, 0 ; Pointeur début tableau mov si, 0 ; Pointeur début tableau
mov di, GUI_MAX_WIDGETS ; Compteur mov di, GUI_MAX_WIDGETS ; Compteur
.loop_widgets: .loop_widgets:
; Est-ce que ce slot est occupé ? ; Est-ce que ce slot est occupé ?
cmp byte [si + widget.state], GUI_STATE_FREE cmp byte [gs:si + widget.state], GUI_STATE_FREE
je .next_widget je .next_widget
; mov ah, [gs:si + widget.state] ; AH = Ancien état du widget
; --- Logique Widget --- ; --- Logique Widget ---
push bx ; Sauver état boutons souris ;push bx ; Sauver état boutons souris
push ax
call gui_update_logic ; Vérifier collision/clic call gui_update_logic ; Vérifier collision/clic
;pop ax
; Si AL=1 (Clic relâché validé), exécuter le callback ; Si AL=1 (Clic relâché validé), exécuter le callback
cmp al, 1 cmp al, 1
jne .no_action jne .no_action
; Vérifier si un callback est défini ; Vérifier si un callback est défini
cmp word [si + widget.callback], 0 cmp word [gs:si + widget.event_click], 0
je .no_action je .no_action
; APPEL DU CALLBACK (Fonction utilisateur) ; APPEL DU CALLBACK (Fonction utilisateur)
; On sauve les registres car le callback peut faire n'importe quoi ; On sauve les registres car le callback peut faire n'importe quoi
pusha pusha
call word [si + widget.callback] call word [gs:si + widget.event_click]
popa popa
.no_action: .no_action:
@ -184,6 +216,7 @@ gui_process_all:
dec di dec di
jnz .loop_widgets jnz .loop_widgets
pop ds
popa popa
ret ret
@ -193,25 +226,25 @@ gui_process_all:
; (Interne) Met à jour l'état d'un seul widget ; (Interne) Met à jour l'état d'un seul widget
; Entrée : SI=Widget, CX=MouseX, DX=MouseY, BL=Buttons ; Entrée : SI=Widget, CX=MouseX, DX=MouseY, BL=Buttons
; Sortie : AL=1 si Clicked, 0 sinon. Met à jour [SI].state ; Sortie : AL=1 si Clicked, 0 sinon. Met à jour [gs:si].state
gui_update_logic: gui_update_logic:
xor ax, ax xor ax, ax
cmp byte [si + widget.state], GUI_STATE_DISABLED cmp byte [gs:si + widget.state], GUI_STATE_DISABLED
je .done je .done
; --- Hit Test --- ; --- Hit Test ---
cmp cx, [si + widget.x] cmp cx, [gs:si + widget.x]
jl .miss jl .miss
mov bp, [si + widget.x] mov bp, [gs:si + widget.x]
add bp, [si + widget.w] add bp, [gs:si + widget.w]
cmp cx, bp cmp cx, bp
jg .miss jg .miss
cmp dx, [si + widget.y] cmp dx, [gs:si + widget.y]
jl .miss jl .miss
mov bp, [si + widget.y] mov bp, [gs:si + widget.y]
add bp, [si + widget.h] add bp, [gs:si + widget.h]
cmp dx, bp cmp dx, bp
jg .miss jg .miss
@ -220,26 +253,26 @@ gui_update_logic:
jz .released jz .released
; Clic enfoncé ; Clic enfoncé
mov byte [si + widget.state], GUI_STATE_PRESSED mov byte [gs:si + widget.state], GUI_STATE_PRESSED
jmp .done jmp .done
.released: .released:
; Bouton relâché. Était-il pressé avant ? ; Bouton relâché. Était-il pressé avant ?
cmp byte [si + widget.state], GUI_STATE_PRESSED cmp byte [gs:si + widget.state], GUI_STATE_PRESSED
jne .hover jne .hover
; Clic validé ! ; Clic validé !
mov al, 1 mov al, 1
mov byte [si + widget.state], GUI_STATE_HOVER mov byte [gs:si + widget.state], GUI_STATE_HOVER
jmp .done jmp .done
.hover: .hover:
mov byte [si + widget.state], GUI_STATE_HOVER mov byte [gs:si + widget.state], GUI_STATE_HOVER
jmp .done jmp .done
.miss: .miss:
mov byte [si + widget.state], GUI_STATE_NORMAL mov byte [gs:si + widget.state], GUI_STATE_NORMAL
.done: .done:
ret ret
; (Interne) Dessine le widget pointé par SI ; (Interne) Dessine le widget pointé par SI
@ -247,10 +280,10 @@ gui_draw_single_widget:
pusha pusha
; Chargement coords ; Chargement coords
mov ax, [si + widget.x] mov ax, [gs:si + widget.x]
mov bx, [si + widget.y] mov bx, [gs:si + widget.y]
mov cx, [si + widget.w] mov cx, [gs:si + widget.w]
mov dx, [si + widget.h] mov dx, [gs:si + widget.h]
; Calcul X2, Y2 ; Calcul X2, Y2
mov di, ax mov di, ax
@ -259,19 +292,19 @@ gui_draw_single_widget:
add bp, dx add bp, dx
; Dispatch selon état ; Dispatch selon état
mov al, [si + widget.state] mov al, [gs:si + widget.state]
cmp al, GUI_STATE_PRESSED cmp al, GUI_STATE_PRESSED
je .paint_pressed je .paint_pressed
cmp al, GUI_STATE_HOVER cmp al, GUI_STATE_HOVER
je .paint_hover je .paint_hover
.paint_normal: .paint_normal:
GFX RECTANGLE_FILL, ax, bx, di, bp, 1 ; Blanc GFX RECTANGLE_FILL, ax, bx, di, bp, 1 ; Blanc
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 ; Bord Noir GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 ; Bord Noir
GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT
jmp .text jmp .text
.paint_hover: .paint_hover:
GFX RECTANGLE_FILL, ax, bx, di, bp, 1 GFX RECTANGLE_FILL, ax, bx, di, bp, 1
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 GFX RECTANGLE_DRAW, ax, bx, di, bp, 0
; Effet gras ; Effet gras
@ -282,36 +315,38 @@ gui_draw_single_widget:
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 GFX RECTANGLE_DRAW, ax, bx, di, bp, 0
; Restauration coords pour le texte ; Restauration coords pour le texte
mov ax, [si + widget.x] mov ax, [gs:si + widget.x]
mov bx, [si + widget.y] mov bx, [gs:si + widget.y]
jmp .text_setup jmp .text_setup
.paint_pressed: .paint_pressed:
GFX RECTANGLE_FILL, ax, bx, di, bp, 0 ; Noir GFX RECTANGLE_FILL, ax, bx, di, bp, 0 ; Noir
GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT
jmp .text jmp .text
.text_setup: .text_setup:
; Petit hack pour recentrer le texte après l'effet gras ; Petit hack pour recentrer le texte après l'effet gras
mov cx, [si + widget.w] mov cx, [gs:si + widget.w]
mov dx, [si + widget.h] mov dx, [gs:si + widget.h]
.text: .text:
; --- Centrage Texte (Simplifié) --- ; --- Centrage Texte (Simplifié) ---
push si push si
mov es, [si + widget.text_seg] mov es, [gs:si + widget.text_seg]
mov di, [si + widget.text_ofs] mov di, [gs:si + widget.text_ofs]
xor cx, cx xor cx, cx
.strlen:
.strlen:
cmp byte [es:di], 0 cmp byte [es:di], 0
je .calc je .calc
inc cx inc cx
inc di inc di
jmp .strlen jmp .strlen
.calc:
.calc:
shl cx, 3 ; Largeur texte px shl cx, 3 ; Largeur texte px
push ax ; Sauve X original push ax ; Sauve X original
mov ax, [si + widget.w] mov ax, [gs:si + widget.w]
sub ax, cx sub ax, cx
shr ax, 1 shr ax, 1
pop bx ; Récupère X original dans BX (oups, on veut ajouter) pop bx ; Récupère X original dans BX (oups, on veut ajouter)
@ -319,17 +354,17 @@ gui_draw_single_widget:
push ax ; X final prêt push ax ; X final prêt
mov bx, [si + widget.h] mov bx, [gs:si + widget.h]
sub bx, 8 sub bx, 8
shr bx, 1 shr bx, 1
add bx, [si + widget.y] ; Y final add bx, [gs:si + widget.y] ; Y final
pop cx ; CX = X Final, BX = Y Final pop cx ; CX = X Final, BX = Y Final
GFX GOTOXY, cx, bx GFX GOTOXY, cx, bx
mov dx, [si + widget.text_seg] mov dx, [gs:si + widget.text_seg]
mov ax, [si + widget.text_ofs] mov ax, [gs:si + widget.text_ofs]
GFX WRITE, dx, ax GFX WRITE, dx, ax
pop si pop si
@ -347,10 +382,10 @@ gui_draw_button:
pusha pusha
; Charger les propriétés depuis la structure ; Charger les propriétés depuis la structure
mov ax, [si + widget.x] mov ax, [gs:si + widget.x]
mov bx, [si + widget.y] mov bx, [gs:si + widget.y]
mov cx, [si + widget.w] mov cx, [gs:si + widget.w]
mov dx, [si + widget.h] mov dx, [gs:si + widget.h]
; Calculer X2 et Y2 pour le rectangle ; Calculer X2 et Y2 pour le rectangle
mov di, ax ; X1 mov di, ax ; X1
@ -359,13 +394,13 @@ gui_draw_button:
add bp, dx ; Y2 = Y + H add bp, dx ; Y2 = Y + H
; Vérifier l'état ; Vérifier l'état
cmp byte [si + widget.state], GUI_STATE_PRESSED cmp byte [gs:si + widget.state], GUI_STATE_PRESSED
je .draw_pressed je .draw_pressed
cmp byte [si + widget.state], GUI_STATE_HOVER cmp byte [gs:si + widget.state], GUI_STATE_HOVER
je .draw_hover je .draw_hover
cmp byte [si + widget.state], GUI_STATE_DISABLED cmp byte [gs:si + widget.state], GUI_STATE_DISABLED
je .draw_disabled je .draw_disabled
.draw_normal: .draw_normal:
@ -392,10 +427,10 @@ gui_draw_button:
GFX RECTANGLE_DRAW, ax, bx, di, bp, 0 GFX RECTANGLE_DRAW, ax, bx, di, bp, 0
; Restaurer les coords pour le texte ; Restaurer les coords pour le texte
mov ax, [si + widget.x] mov ax, [gs:si + widget.x]
mov bx, [si + widget.y] mov bx, [gs:si + widget.y]
mov di, ax mov di, ax
add di, [si + widget.w] add di, [gs:si + widget.w]
GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT
jmp .draw_text jmp .draw_text
@ -425,8 +460,8 @@ gui_draw_button:
push si push si
; 1. Calculer longueur chaine ; 1. Calculer longueur chaine
mov es, [si + widget.text_seg] mov es, [gs:si + widget.text_seg]
mov di, [si + widget.text_ofs] mov di, [gs:si + widget.text_ofs]
xor cx, cx xor cx, cx
.strlen: .strlen:
@ -440,21 +475,21 @@ gui_draw_button:
; CX = Longueur chaine ; CX = Longueur chaine
shl cx, 3 ; CX * 8 (largeur en pixels du texte) shl cx, 3 ; CX * 8 (largeur en pixels du texte)
mov ax, [si + widget.w] mov ax, [gs:si + widget.w]
sub ax, cx ; Marge horizontale totale sub ax, cx ; Marge horizontale totale
shr ax, 1 ; Diviser par 2 shr ax, 1 ; Diviser par 2
add ax, [si + widget.x] ; X final add ax, [gs:si + widget.x] ; X final
mov bx, [si + widget.h] mov bx, [gs:si + widget.h]
sub bx, 8 ; Hauteur fonte (8px) sub bx, 8 ; Hauteur fonte (8px)
shr bx, 1 ; Diviser par 2 shr bx, 1 ; Diviser par 2
add bx, [si + widget.y] ; Y final add bx, [gs:si + widget.y] ; Y final
; 2. Dessiner ; 2. Dessiner
GFX GOTOXY, ax, bx GFX GOTOXY, ax, bx
mov dx, [si + widget.text_seg] mov dx, [gs:si + widget.text_seg]
mov ax, [si + widget.text_ofs] mov ax, [gs:si + widget.text_ofs]
GFX WRITE, dx, ax GFX WRITE, dx, ax
pop si pop si
@ -482,27 +517,27 @@ gui_update_button:
xor ax, ax ; AL = Return value (0) xor ax, ax ; AL = Return value (0)
; Si désactivé, on ne fait rien ; Si désactivé, on ne fait rien
cmp byte [si + widget.state], GUI_STATE_DISABLED cmp byte [gs:si + widget.state], GUI_STATE_DISABLED
je .quit je .quit
; 1. Test de collision (Hit Test) ; 1. Test de collision (Hit Test)
; X >= Widget.X ? ; X >= Widget.X ?
cmp cx, [si + widget.x] cmp cx, [gs:si + widget.x]
jl .no_hit jl .no_hit
; X <= Widget.X + Widget.W ? ; X <= Widget.X + Widget.W ?
mov di, [si + widget.x] mov di, [gs:si + widget.x]
add di, [si + widget.w] add di, [gs:si + widget.w]
cmp cx, di cmp cx, di
jg .no_hit jg .no_hit
; Y >= Widget.Y ? ; Y >= Widget.Y ?
cmp dx, [si + widget.y] cmp dx, [gs:si + widget.y]
jl .no_hit jl .no_hit
; Y <= Widget.Y + Widget.H ? ; Y <= Widget.Y + Widget.H ?
mov di, [si + widget.y] mov di, [gs:si + widget.y]
add di, [si + widget.h] add di, [gs:si + widget.h]
cmp dx, di cmp dx, di
jg .no_hit jg .no_hit
@ -513,27 +548,27 @@ gui_update_button:
jz .hovering jz .hovering
; -> Clic maintenu ; -> Clic maintenu
mov byte [si + widget.state], GUI_STATE_PRESSED mov byte [gs:si + widget.state], GUI_STATE_PRESSED
jmp .draw_update jmp .draw_update
.hovering: .hovering:
; Souris dessus, mais pas de clic ; Souris dessus, mais pas de clic
; Si on était en PRESSED juste avant, c'est un "CLICK" valide (Relâchement) ; Si on était en PRESSED juste avant, c'est un "CLICK" valide (Relâchement)
cmp byte [si + widget.state], GUI_STATE_PRESSED cmp byte [gs:si + widget.state], GUI_STATE_PRESSED
jne .just_hover jne .just_hover
; C'est un click valide ! ; C'est un click valide !
mov al, 1 ; Return CLICKED mov al, 1 ; Return CLICKED
mov byte [si + widget.state], GUI_STATE_HOVER mov byte [gs:si + widget.state], GUI_STATE_HOVER
jmp .draw_update jmp .draw_update
.just_hover: .just_hover:
mov byte [si + widget.state], GUI_STATE_HOVER mov byte [gs:si + widget.state], GUI_STATE_HOVER
jmp .draw_update jmp .draw_update
.no_hit: .no_hit:
; Souris en dehors ; Souris en dehors
mov byte [si + widget.state], GUI_STATE_NORMAL mov byte [gs:si + widget.state], GUI_STATE_NORMAL
.draw_update: .draw_update:
; Appelle la fonction de dessin pour mettre à jour le visuel ; Appelle la fonction de dessin pour mettre à jour le visuel