diff --git a/doc/assets/Capture-2026-02-05-203804.png b/doc/assets/Capture-2026-02-05-203804.png new file mode 100644 index 0000000..8bf076b Binary files /dev/null and b/doc/assets/Capture-2026-02-05-203804.png differ diff --git a/readme.md b/readme.md index 280e312..20491b6 100644 --- a/readme.md +++ b/readme.md @@ -13,8 +13,13 @@ Rather than booting an operating system from disk, the PC starts **directly from The project is intentionally educational, experimental, and minimalist. ![Screenshot of the boot + text test](/doc/assets/Capture-2026-01-29-210122.png) +Screenshot of early boot version + Text functions ![Screenshot WIP - Buttons & Sliders](/doc/assets/Capture-2026-02-03-213148.png) +Screenshot of early buttons & sliders + +![Screenshot WIP - Rounded Buttons & Checkboxes ](/doc/assets/Capture-2026-01-29-210122.png) +Screenshot of rounded buttons & checkboxes --- @@ -138,8 +143,13 @@ Au lieu de charger un système d’exploitation depuis un disque, le PC **démar Le projet est volontairement **éducatif, expérimental et minimaliste**. ![Screenshot of the boot + text test](/doc/assets/Capture-2026-01-29-210122.png) +Screenshot of early boot version + Text functions ![Screenshot WIP - Buttons & Sliders](/doc/assets/Capture-2026-02-03-213148.png) +Screenshot of early buttons & sliders + +![Screenshot WIP - Rounded Buttons & Checkboxes ](/doc/assets/Capture-2026-01-29-210122.png) +Screenshot of rounded buttons & checkboxes --- diff --git a/src/boot.asm b/src/boot.asm index c26ba97..cc47b1b 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -47,8 +47,7 @@ section .text %include "./common/bda.asm" ; %include "./common/debug_cga.asm" -; %include "./drivers/gfx_cgam.asm" -%include "./drivers/gfx_egam.asm" +%include "./drivers/gfx_cgam.asm" %include "./services/gui_lib.asm" %include "./drivers/mouse_ps2.asm" %include "./drivers/keyboard_ps2.asm" @@ -134,7 +133,7 @@ build_interface: 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.h], 16 mov word [gs:si + widget.text_ofs], str_quit ; mov byte [gs:si + widget.user_id], 1 ; Style "Bouton par défaut" (OK) mov word [gs:si + widget.text_seg], cs ; Texte est dans la ROM @@ -148,24 +147,48 @@ build_interface: mov word [gs:si + widget.x], 100 mov word [gs:si + widget.y], 50 mov word [gs:si + widget.w], 80 - mov word [gs:si + widget.h], 20 + mov word [gs:si + widget.h], 16 mov word [gs:si + widget.text_ofs], str_hello mov word [gs:si + widget.text_seg], cs mov byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON - ; Créer Bouton 2 "HELLO" + ; Créer checkbox 3 "option 1" call gui_alloc_widget jc .mem_full - mov word [gs:si + widget.x], 100 - mov word [gs:si + widget.y], 150 - 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.x], 200 + mov word [gs:si + widget.y], 50 + mov word [gs:si + widget.w], 100 + mov word [gs:si + widget.h], 15 + mov word [gs:si + widget.text_ofs], str_option1 mov word [gs:si + widget.text_seg], cs - mov byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON + mov byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX ; Pas de callback + ; Créer checkbox 3 "option 2" + call gui_alloc_widget + jc .mem_full + + mov word [gs:si + widget.x], 200 + mov word [gs:si + widget.y], 50+16 + mov word [gs:si + widget.w], 100 + mov word [gs:si + widget.h], 15 + mov word [gs:si + widget.text_ofs], str_option2 + mov word [gs:si + widget.text_seg], cs + mov byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX + + ; Créer checkbox 3 "option 2" + call gui_alloc_widget + jc .mem_full + + mov word [gs:si + widget.x], 200 + mov word [gs:si + widget.y], 50+16*2 + mov word [gs:si + widget.w], 100 + mov word [gs:si + widget.h], 15 + mov word [gs:si + widget.text_ofs], str_option3 + mov word [gs:si + widget.text_seg], cs + mov byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX + ; Créer Slider (Drag) call gui_alloc_widget jc .mem_full @@ -188,8 +211,11 @@ build_interface: ; --- Données ROM --- -str_quit db "QUITTER", 0 -str_hello db "HELLO", 0 +str_quit db "Quitter", 0 +str_hello db "Hello", 0 +str_option1 db "option 1", 0 +str_option2 db "option 2", 0 +str_option3 db "option 3", 0 str_drag db "->", 0 ; ----------------------------------------------------------- diff --git a/src/services/gui_lib.asm b/src/services/gui_lib.asm index 3aaf471..531a813 100644 --- a/src/services/gui_lib.asm +++ b/src/services/gui_lib.asm @@ -20,10 +20,13 @@ ; ; ============================================================================= -; --- Configuration Mémoire --- +; --- Configuration --- %define GUI_RAM_SEG 0x0A00 ; Segment de données UI %define GUI_MAX_WIDGETS 32 ; Nombre max de widgets simultanés +; Dimensions +%define GUI_CHECKBOX_SIZE 10 ; + ; --- Drapeaux & États --- %define GUI_STATE_FREE 0 ; Le slot est vide (mémoire dispo) %define GUI_STATE_NORMAL 1 ; Affiché, repos @@ -36,6 +39,7 @@ %define WIDGET_TYPE_SLIDER 1 %define WIDGET_TYPE_LABEL 2 %define WIDGET_TYPE_ROUND_BUTTON 3 +%define WIDGET_TYPE_CHECKBOX 4 ; --- Widgets attributs --- %define BUTTON_OK 1 @@ -307,6 +311,11 @@ gui_update_logic: jmp .done .case_4: + cmp byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX + jne .case_5 + call gui_logic_checkbox + + .case_5: jmp .done .miss: ; Si on n'est plus dessus, mais qu'on l'était avant (HOVER/PRESSED), il faut redessiner ! @@ -434,6 +443,33 @@ gui_logic_slider: xor ax, ax ret +; --- Logique spécifique Checkbox --- +gui_logic_checkbox: + test bl, 1 ; Clic gauche ? + jz .released + + ; Clic enfoncé + mov byte [gs:si + widget.state], GUI_STATE_PRESSED + xor ax, ax + ret + + .released: + cmp byte [gs:si + widget.state], GUI_STATE_PRESSED + jne .hover + + ; Clic validé ! Toggle value + xor word [gs:si + widget.attr_val], 1 + mov byte [gs:si + widget.oldstate], 255 ; Force redraw + + mov al, 1 + mov byte [gs:si + widget.state], GUI_STATE_HOVER + ret + + .hover: + mov byte [gs:si + widget.state], GUI_STATE_HOVER + xor ax, ax + ret + ; Dessine le widget pointé par SI @@ -478,9 +514,15 @@ gui_draw_single_widget: cmp byte [gs:si + widget.type], WIDGET_TYPE_ROUND_BUTTON jne .case_4 call draw_round_button -; jmp .done + jmp .done .case_4: + cmp byte [gs:si + widget.type], WIDGET_TYPE_CHECKBOX + jne .case_5 + call draw_checkbox + jmp .done + + .case_5: @@ -612,22 +654,23 @@ draw_round_button: .paint_normal: GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE mov di, 0 ; Couleur Noire - call .draw_round_borders + call draw_round_borders GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT jmp .draw_text_now .paint_hover: GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE + GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 + ; GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE_LIGHT mov di, 0 ; Couleur Noire - GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE_LIGHT - call .draw_round_borders + call draw_round_borders GFX TXT_MODE, GFX_TXT_BLACK_TRANSPARENT jmp .draw_text_now .paint_pressed: GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_BLACK mov di, 1 ; Bordure Blanche sur fond noir - call .draw_round_borders + call draw_round_borders GFX TXT_MODE, GFX_TXT_WHITE_TRANSPARENT jmp .draw_text_now @@ -641,9 +684,8 @@ draw_round_button: .done: ret -.draw_round_borders: +draw_round_borders: ; Bordure principale - GFX RECTANGLE_ROUND, ax, bx, cx, dx, di ; Si style "OK" (attr_mode & BUTTON_OK), on ajoute la double bordure ;test byte [gs:si + widget.attr_mode], BUTTON_OK ;jz .done_borders @@ -655,6 +697,93 @@ draw_round_button: .done_borders: ret +draw_checkbox: + ; met un "fond" blanc + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE + + cmp byte [gs:si + widget.state], GUI_STATE_HOVER + jne .no_hover + GFX RECTANGLE_ROUND, ax, bx, cx, dx, 0 + .no_hover: + + ; Calcul coords box (centré verticalement) + mov ax, [gs:si + widget.x] + add ax, 4 + mov bx, [gs:si + widget.y] + + mov cx, [gs:si + widget.h] + mov dx, [gs:si + widget.w] + + sub cx, GUI_CHECKBOX_SIZE + shr cx, 1 + add bx, cx ; Y1 + + mov cx, ax + add cx, GUI_CHECKBOX_SIZE ; X2 + + mov dx, bx + add dx, GUI_CHECKBOX_SIZE ; Y2 + + ; Sauvegarde coords pour le X + push ax + push bx + push cx + push dx + + ; Fond blanc + Bordure noire + GFX RECTANGLE_FILL, ax, bx, cx, dx, PATTERN_WHITE + GFX RECTANGLE, ax, bx, cx, dx, 0 + + ; Check if checked + cmp word [gs:si + widget.attr_val], 0 + je .draw_label + + ; Dessin du X (Diagonales) + add ax, 2 + add bx, 2 + sub cx, 2 + sub dx, 2 + + push ax + push bx + push dx + .loop_x: + cmp ax, cx + jg .end_x + GFX PUTPIXEL, ax, bx, 0 + GFX PUTPIXEL, ax, dx, 0 + inc ax + inc bx + dec dx + jmp .loop_x + .end_x: + pop dx + pop bx + pop ax + + .draw_label: + pop dx + pop cx + pop bx + pop ax + + ; Dessin du texte à droite (BoxX2 + 6) + add cx, 6 + + ; Y = Centré par rapport au widget + mov bx, [gs:si + widget.y] + mov ax, [gs:si + widget.h] + sub ax, 8 + shr ax, 1 + add bx, ax + inc bx + + GFX GOTOXY, cx, bx + mov dx, [gs:si + widget.text_seg] + mov ax, [gs:si + widget.text_ofs] + GFX WRITE, dx, ax + + ret ; ; affiche le texte au centre du widget gs:si ; @@ -691,6 +820,7 @@ draw_text: sub bx, 8 shr bx, 1 add bx, [gs:si + widget.y] ; Y final + inc bx pop cx ; CX = X Final, BX = Y Final