Il reste beaucoup d'améliorations et de codage à faire avant d'avoir une interface très fonctionnel mais ca montre les possibilitées de la lib Canvas pour faire des interfaces un peut plus pro.
Le code devrait être Cross platforme car ca n'utilise que les commandes natif de PB.
Code : Tout sélectionner
; ============================================================================
; By Metalos
; UI avec Canvas + petits Canvas-boutons
; chaque zone = ContainerGadget dans les splitters.
; PureBasic 6.x (macOS/Win/Linux)
; ============================================================================
EnableExplicit
;============================= IDs =====================================
#WIN_MAIN = 0
; Containers mis dans les splitters
#CNT_Header = 10
#CNT_Left = 11
#CNT_Viewport = 12
#CNT_Right = 13
#CNT_Timeline = 14
; Canvas de fond (dessin)
#CV_Header = 20
#CV_Left = 21
#CV_Viewport = 22
#CV_Right = 23
#CV_Timeline = 24
; Splitters
#SP_CenterRight = 30
#SP_LeftCenter = 31
#SP_MainBottom = 32
; Boutons (Canvas) — enfants des containers correspondants
; Header
#BTN_File = 100
#BTN_Edit = 101
#BTN_Render = 102
#BTN_Window = 103
#BTN_Help = 104
; Left panel
#BTN_AddObj = 110
#BTN_DelObj = 111
; Viewport
#BTN_ViewFront = 120
#BTN_ViewTop = 121
#BTN_ViewSide = 122
#BTN_ViewPersp = 123
#BTN_ZoomFit = 124
; Right panel tabs
#BTN_TabRender = 130
#BTN_TabScene = 131
#BTN_TabWorld = 132
#BTN_TabObject = 133
#BTN_TabModifiers = 134
; Timeline
#BTN_Stop = 140
#BTN_Play = 141
#BTN_Prev = 142
#BTN_Next = 143
#BTN_Loop = 144
; Timer
#TMR_Play = 1
;============================= State ===================================
Global zoom.f = 1.0
Global gridSize = 32
Global camW = 640, camH = 360
Global playing = #False
Global frameStart = 1, frameEnd = 462
Global currentFrame = 1
Global dragTimeline = #False
;============================= Theme ===================================
Structure ColorSet
bg.i : mid.i : panel.i : panel2.i : accent.i : line.i : text.i : textDim.i
EndStructure
Global theme.ColorSet
theme\bg = $1E1E1E
theme\mid = $262626
theme\panel = $2E2E2E
theme\panel2 = $353535
theme\accent = $FFAA00
theme\line = $404040
theme\text = $EAEAEA
theme\textDim= $B0B0B0
;============================= Helpers =================================
Procedure.i ClampI(v.i, a.i, b.i)
If v < a : ProcedureReturn a : EndIf
If v > b : ProcedureReturn b : EndIf
ProcedureReturn v
EndProcedure
Procedure.f ClampF(v.f, a.f, b.f)
If v < a : ProcedureReturn a : EndIf
If v > b : ProcedureReturn b : EndIf
ProcedureReturn v
EndProcedure
Procedure DrawButton(id, txt$)
If StartDrawing(CanvasOutput(id))
Box(0,0,OutputWidth(),OutputHeight(), $3A3A3A)
DrawingMode(#PB_2DDrawing_Transparent)
DrawingFont(FontID(0))
DrawText((OutputWidth()-TextWidth(txt$))/2, (OutputHeight()-TextHeight(txt$))/2, txt$, theme\text)
StopDrawing()
EndIf
EndProcedure
;============================= Drawing (fond des zones) =================
Procedure DrawHeader()
If StartDrawing(CanvasOutput(#CV_Header))
Box(0,0,OutputWidth(),OutputHeight(), theme\mid)
Box(0,0,OutputWidth(),2, theme\accent)
DrawingFont(FontID(0)) : DrawingMode(#PB_2DDrawing_Transparent)
Define s$ = "Ve:136 | Fa:93 | Ob:40-1 | La:16 | Mem:241.63M"
DrawText(OutputWidth()-TextWidth(s$)-12,7,s$, theme\textDim)
StopDrawing()
EndIf
EndProcedure
Procedure DrawLeft()
If StartDrawing(CanvasOutput(#CV_Left))
Box(0,0,OutputWidth(),OutputHeight(), theme\panel)
Box(0,0,OutputWidth(),24, theme\panel2)
DrawingFont(FontID(0)) : DrawingMode(#PB_2DDrawing_Transparent)
DrawText(8,5,"Outliner", theme\text)
Define y = 30, i
For i=0 To 50
If y > OutputHeight()-18 : Break : EndIf
DrawText(10,y,"• Empty_"+RSet(Str(i),2,"0"), theme\textDim)
y + 16
Next
Box(OutputWidth()-1,0,1,OutputHeight(), theme\line)
StopDrawing()
EndIf
EndProcedure
Procedure DrawViewport()
If StartDrawing(CanvasOutput(#CV_Viewport))
Box(0,0,OutputWidth(),OutputHeight(), theme\bg)
DrawingFont(FontID(0))
Define s = Round(gridSize*zoom,#PB_Round_Nearest)
If s < 10 : s = 10 : EndIf
Define x, y
x = 0 : While x <= OutputWidth() : Line(x,0,0,OutputHeight(), theme\line) : x + s : Wend
y = 0 : While y <= OutputHeight() : Line(0,y,OutputWidth(),0, theme\line) : y + s : Wend
Define w = camW*zoom, h = camH*zoom
If w>OutputWidth()-20 : w = OutputWidth()-20 : EndIf
If h>OutputHeight()-20 : h = OutputHeight()-20 : EndIf
Define cx = OutputWidth()/2 - w/2
Define cy = OutputHeight()/2 - h/2
Box(cx,cy,w,h, RGB(0,0,0))
Box(cx+1,cy+1,w-2,h-2, theme\panel2)
Box(cx+10,cy+10,w-20,h-20, theme\mid)
Protected rx = w/3, ry = h/2
If rx > 0 And ry > 0 : Ellipse(cx+w/2, cy+h/2, rx, ry, $5A5A5A) : EndIf
If w > 60 And h > 60
Ellipse(cx+w/2-30, cy+h/2-40, 18, 12, $FFFFFF)
Ellipse(cx+w/2+30, cy+h/2-40, 18, 12, $FFFFFF)
Ellipse(cx+w/2-30, cy+h/2-40, 6, 6, $000000)
Ellipse(cx+w/2+30, cy+h/2-40, 6, 6, $000000)
EndIf
DrawingMode(#PB_2DDrawing_Transparent)
DrawText(8,6,"Camera Persp", theme\textDim)
Box(0,0,OutputWidth(),1, theme\line)
Box(OutputWidth()-1,0,1,OutputHeight(), theme\line)
StopDrawing()
EndIf
EndProcedure
Procedure DrawRight()
If StartDrawing(CanvasOutput(#CV_Right))
Box(0,0,OutputWidth(),OutputHeight(), theme\panel)
Box(0,0,OutputWidth(),28, theme\panel2)
DrawingFont(FontID(0)) : DrawingMode(#PB_2DDrawing_Transparent)
Define y = 40, i
For i=0 To 5
Box(10,y,OutputWidth()-20,26, theme\mid)
DrawText(18,y+6,"Section "+Str(i+1), theme\textDim)
y + 36
If y>OutputHeight()-36 : Break : EndIf
Next
Box(0,0,1,OutputHeight(), theme\line)
StopDrawing()
EndIf
EndProcedure
Procedure DrawTimeline()
If StartDrawing(CanvasOutput(#CV_Timeline))
Box(0,0,OutputWidth(),OutputHeight(), theme\panel)
Box(0,0,OutputWidth(),22, theme\panel2)
DrawingFont(FontID(0)) : DrawingMode(#PB_2DDrawing_Transparent)
DrawText(8,4,"Timeline", theme\text)
Define top = 24, h = OutputHeight()-top-8
Box(0,top,OutputWidth(),h, theme\mid)
Define i, stepFrames = 10
Define pxPerFrame.f = (OutputWidth()-20) / (frameEnd-frameStart+0.0)
i = frameStart
While i <= frameEnd
Define x = 10 + (i-frameStart) * pxPerFrame
Line(x, top, 0, 8, theme\line)
DrawText(x-TextWidth(Str(i))/2, top+10, Str(i), theme\textDim)
i + stepFrames
Wend
currentFrame = ClampI(currentFrame, frameStart, frameEnd)
Define cx = 10 + (currentFrame-frameStart) * pxPerFrame
Box(cx-1, top, 3, h, theme\accent)
DrawText(OutputWidth()-120, 4, "Frame: "+Str(currentFrame), theme\text)
StopDrawing()
EndIf
EndProcedure
;============================= Callbacks (click-only) ==================
Macro ClickOnly()
If EventType() <> #PB_EventType_LeftButtonDown : ProcedureReturn : EndIf
EndMacro
Procedure CB_File() : ClickOnly() : Debug "File" : EndProcedure
Procedure CB_Edit() : ClickOnly() : Debug "Edit" : EndProcedure
Procedure CB_Render() : ClickOnly() : Debug "Render" : EndProcedure
Procedure CB_Window() : ClickOnly() : Debug "Window" : EndProcedure
Procedure CB_Help() : ClickOnly() : Debug "Help" : EndProcedure
Procedure CB_AddObj() : ClickOnly() : Debug "+ Add object" : EndProcedure
Procedure CB_DelObj() : ClickOnly() : Debug "- Delete object" : EndProcedure
Procedure CB_ViewFront() : ClickOnly() : Debug "View: Front" : EndProcedure
Procedure CB_ViewTop() : ClickOnly() : Debug "View: Top" : EndProcedure
Procedure CB_ViewSide() : ClickOnly() : Debug "View: Side" : EndProcedure
Procedure CB_ViewPersp() : ClickOnly() : Debug "View: Persp" : EndProcedure
Procedure CB_ZoomFit() : ClickOnly() : Debug "Zoom to Fit" : EndProcedure
Procedure CB_TabRender() : ClickOnly() : Debug "Tab: Render" : EndProcedure
Procedure CB_TabScene() : ClickOnly() : Debug "Tab: Scene" : EndProcedure
Procedure CB_TabWorld() : ClickOnly() : Debug "Tab: World" : EndProcedure
Procedure CB_TabObject() : ClickOnly() : Debug "Tab: Object" : EndProcedure
Procedure CB_TabModifiers() : ClickOnly() : Debug "Tab: Modifiers" : EndProcedure
Procedure CB_Stop()
ClickOnly()
playing = #False
RemoveWindowTimer(#WIN_MAIN, #TMR_Play)
DrawTimeline()
Debug "Stop"
EndProcedure
Procedure CB_Play()
ClickOnly()
playing ! 1
If playing
AddWindowTimer(#WIN_MAIN, #TMR_Play, 30)
Debug "Play"
Else
RemoveWindowTimer(#WIN_MAIN, #TMR_Play)
Debug "Pause"
EndIf
DrawTimeline()
EndProcedure
Procedure CB_Prev()
ClickOnly()
currentFrame - 1 : If currentFrame < frameStart : currentFrame = frameStart : EndIf
DrawTimeline()
Debug "Prev -> "+Str(currentFrame)
EndProcedure
Procedure CB_Next()
ClickOnly()
currentFrame + 1 : If currentFrame > frameEnd : currentFrame = frameEnd : EndIf
DrawTimeline()
Debug "Next -> "+Str(currentFrame)
EndProcedure
Procedure CB_Loop() : ClickOnly() : Debug "Loop toggle (fake)" : EndProcedure
;============================= Events des Canvas de fond ===============
Procedure HandleViewportEvent()
Select EventType()
Case #PB_EventType_MouseWheel
Protected delta = GetGadgetAttribute(#CV_Viewport, #PB_Canvas_WheelDelta)
If delta > 0 : zoom + 0.1 : Else : zoom - 0.1 : EndIf
zoom = ClampF(zoom, 0.3, 3.0)
DrawViewport()
Case #PB_EventType_Resize
ResizeGadget(#CV_Viewport, #PB_Ignore, #PB_Ignore, GadgetWidth(#CNT_Viewport), GadgetHeight(#CNT_Viewport))
DrawViewport()
EndSelect
EndProcedure
Procedure HandleTimelineEvent()
Protected et = EventType()
Protected mx = GetGadgetAttribute(#CV_Timeline, #PB_Canvas_MouseX)
Protected my = GetGadgetAttribute(#CV_Timeline, #PB_Canvas_MouseY)
Protected top = 24
Protected pxPerFrame.f
Select et
Case #PB_EventType_LeftButtonDown
If my >= top
dragTimeline = #True
pxPerFrame = (GadgetWidth(#CV_Timeline)-20) / (frameEnd-frameStart+0.0)
currentFrame = frameStart + Int((mx-10) / pxPerFrame)
DrawTimeline()
EndIf
Case #PB_EventType_LeftButtonUp
dragTimeline = #False
Case #PB_EventType_MouseMove
If dragTimeline And my >= top
pxPerFrame = (GadgetWidth(#CV_Timeline)-20) / (frameEnd-frameStart+0.0)
currentFrame = frameStart + Int((mx-10) / pxPerFrame)
DrawTimeline()
EndIf
Case #PB_EventType_Resize
ResizeGadget(#CV_Timeline, #PB_Ignore, #PB_Ignore, GadgetWidth(#CNT_Timeline), GadgetHeight(#CNT_Timeline))
DrawTimeline()
EndSelect
EndProcedure
;============================= Layout ==================================
Procedure DrawAllBackgrounds()
ResizeGadget(#CV_Header, 0, 0, GadgetWidth(#CNT_Header), GadgetHeight(#CNT_Header))
ResizeGadget(#CV_Left, 0, 0, GadgetWidth(#CNT_Left), GadgetHeight(#CNT_Left))
ResizeGadget(#CV_Viewport, 0, 0, GadgetWidth(#CNT_Viewport), GadgetHeight(#CNT_Viewport))
ResizeGadget(#CV_Right, 0, 0, GadgetWidth(#CNT_Right), GadgetHeight(#CNT_Right))
ResizeGadget(#CV_Timeline, 0, 0, GadgetWidth(#CNT_Timeline), GadgetHeight(#CNT_Timeline))
DrawHeader() : DrawLeft() : DrawViewport() : DrawRight() : DrawTimeline()
EndProcedure
Procedure ApplyInitialLayout()
Protected w = WindowWidth(#WIN_MAIN)
Protected h = WindowHeight(#WIN_MAIN)
Protected headH = 28
Protected leftW = ClampI(Int(w * 0.23), 220, 420)
Protected rightW = ClampI(Int(w * 0.26), 240, 520)
Protected topH = ClampI(h - headH - 140, 240, h - headH - 90)
SetGadgetState(#SP_LeftCenter, leftW)
SetGadgetState(#SP_CenterRight, w - rightW)
SetGadgetState(#SP_MainBottom, topH)
While WindowEvent() : Wend
DrawAllBackgrounds()
EndProcedure
Procedure ResizeUI()
Protected w = WindowWidth(#WIN_MAIN)
Protected h = WindowHeight(#WIN_MAIN)
ResizeGadget(#CNT_Header, 0,0, w, 28)
ResizeGadget(#SP_MainBottom, 0,28, w, h-28)
DrawAllBackgrounds()
EndProcedure
;============================= Main ====================================
If OpenWindow(#WIN_MAIN, 0,0, 1200, 730, "Blender-like UI (Canvas)", #PB_Window_SystemMenu|#PB_Window_SizeGadget|#PB_Window_ScreenCentered)
If LoadFont(0, "Arial", 10, #PB_Font_HighQuality)
SetGadgetFont(#PB_Default, FontID(0))
EndIf
; --- Containers hiérarchiques ---
ContainerGadget(#CNT_Header, 0,0, 100,28, #PB_Container_Flat)
CanvasGadget(#CV_Header, 0,0, 100,28, #PB_Canvas_Keyboard)
CanvasGadget(#BTN_File, 10,4, 50,20) : DrawButton(#BTN_File,"File") : BindGadgetEvent(#BTN_File, @CB_File())
CanvasGadget(#BTN_Edit, 64,4, 50,20) : DrawButton(#BTN_Edit,"Edit") : BindGadgetEvent(#BTN_Edit, @CB_Edit())
CanvasGadget(#BTN_Render,118,4, 60,20) : DrawButton(#BTN_Render,"Render") : BindGadgetEvent(#BTN_Render, @CB_Render())
CanvasGadget(#BTN_Window,184,4, 60,20) : DrawButton(#BTN_Window,"Window") : BindGadgetEvent(#BTN_Window, @CB_Window())
CanvasGadget(#BTN_Help, 250,4, 50,20) : DrawButton(#BTN_Help,"Help") : BindGadgetEvent(#BTN_Help, @CB_Help())
CloseGadgetList()
ContainerGadget(#CNT_Left, 0,0, 260,100, #PB_Container_Flat)
CanvasGadget(#CV_Left, 0,0, 260,100, #PB_Canvas_Keyboard)
CanvasGadget(#BTN_AddObj, 10,30,80,22) : DrawButton(#BTN_AddObj,"+ Add") : BindGadgetEvent(#BTN_AddObj,@CB_AddObj())
CanvasGadget(#BTN_DelObj,100,30,80,22) : DrawButton(#BTN_DelObj,"- Del") : BindGadgetEvent(#BTN_DelObj,@CB_DelObj())
CloseGadgetList()
ContainerGadget(#CNT_Viewport, 0,0, 640,100, #PB_Container_Flat)
CanvasGadget(#CV_Viewport, 0,0, 640,100, #PB_Canvas_Keyboard)
BindGadgetEvent(#CV_Viewport, @HandleViewportEvent())
CanvasGadget(#BTN_ViewFront, 10,8,56,18) : DrawButton(#BTN_ViewFront,"Front") : BindGadgetEvent(#BTN_ViewFront,@CB_ViewFront())
CanvasGadget(#BTN_ViewTop, 70,8,56,18) : DrawButton(#BTN_ViewTop,"Top") : BindGadgetEvent(#BTN_ViewTop, @CB_ViewTop())
CanvasGadget(#BTN_ViewSide, 130,8,56,18) : DrawButton(#BTN_ViewSide,"Side") : BindGadgetEvent(#BTN_ViewSide, @CB_ViewSide())
CanvasGadget(#BTN_ViewPersp,190,8,56,18) : DrawButton(#BTN_ViewPersp,"Persp") : BindGadgetEvent(#BTN_ViewPersp,@CB_ViewPersp())
CanvasGadget(#BTN_ZoomFit, 250,8,72,18) : DrawButton(#BTN_ZoomFit,"ZoomFit") : BindGadgetEvent(#BTN_ZoomFit, @CB_ZoomFit())
CloseGadgetList()
ContainerGadget(#CNT_Right, 0,0, 300,100, #PB_Container_Flat)
CanvasGadget(#CV_Right, 0,0, 300,100, #PB_Canvas_Keyboard)
CanvasGadget(#BTN_TabRender, 10,4,60,20) : DrawButton(#BTN_TabRender,"Render") : BindGadgetEvent(#BTN_TabRender, @CB_TabRender())
CanvasGadget(#BTN_TabScene, 74,4,60,20) : DrawButton(#BTN_TabScene,"Scene") : BindGadgetEvent(#BTN_TabScene, @CB_TabScene())
CanvasGadget(#BTN_TabWorld, 138,4,60,20) : DrawButton(#BTN_TabWorld,"World") : BindGadgetEvent(#BTN_TabWorld, @CB_TabWorld())
CanvasGadget(#BTN_TabObject, 202,4,60,20) : DrawButton(#BTN_TabObject,"Object") : BindGadgetEvent(#BTN_TabObject, @CB_TabObject())
CanvasGadget(#BTN_TabModifiers,266,4,76,20) : DrawButton(#BTN_TabModifiers,"Modifiers"): BindGadgetEvent(#BTN_TabModifiers,@CB_TabModifiers())
CloseGadgetList()
ContainerGadget(#CNT_Timeline, 0,0, 100,120, #PB_Container_Flat)
CanvasGadget(#CV_Timeline, 0,0, 100,120, #PB_Canvas_Keyboard)
BindGadgetEvent(#CV_Timeline, @HandleTimelineEvent())
CanvasGadget(#BTN_Stop, 100,2,40,20) : DrawButton(#BTN_Stop,"Stop") : BindGadgetEvent(#BTN_Stop,@CB_Stop())
CanvasGadget(#BTN_Play, 144,2,40,20) : DrawButton(#BTN_Play,"Play") : BindGadgetEvent(#BTN_Play,@CB_Play())
CanvasGadget(#BTN_Prev, 188,2,40,20) : DrawButton(#BTN_Prev,"Prev") : BindGadgetEvent(#BTN_Prev,@CB_Prev())
CanvasGadget(#BTN_Next, 232,2,40,20) : DrawButton(#BTN_Next,"Next") : BindGadgetEvent(#BTN_Next,@CB_Next())
CanvasGadget(#BTN_Loop, 276,2,50,20) : DrawButton(#BTN_Loop,"Loop") : BindGadgetEvent(#BTN_Loop,@CB_Loop())
CloseGadgetList()
; --- Splitters : contiennent les CONTAINERS du bloc central ---
SplitterGadget(#SP_CenterRight, 0,0, 300,200, #CNT_Viewport, #CNT_Right, #PB_Splitter_Vertical|#PB_Splitter_Separator)
SetGadgetAttribute(#SP_CenterRight, #PB_Splitter_FirstMinimumSize, 300)
SetGadgetAttribute(#SP_CenterRight, #PB_Splitter_SecondMinimumSize, 220)
SplitterGadget(#SP_LeftCenter, 0,0, 300,200, #CNT_Left, #SP_CenterRight, #PB_Splitter_Vertical|#PB_Splitter_Separator)
SetGadgetAttribute(#SP_LeftCenter, #PB_Splitter_FirstMinimumSize, 200)
; ➜ CORRECTION: le splitter principal contient le bloc central en haut et la timeline en bas
SplitterGadget(#SP_MainBottom, 0,0, 300,300, #SP_LeftCenter, #CNT_Timeline, #PB_Splitter_Separator)
; Positionner header hors splitter principal
ResizeGadget(#CNT_Header, 0,0, WindowWidth(#WIN_MAIN), 28)
ResizeGadget(#SP_MainBottom, 0,28, WindowWidth(#WIN_MAIN), WindowHeight(#WIN_MAIN)-28)
; Layout initial proportionnel + dessins
ApplyInitialLayout()
; ====================== Event Loop =====================
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
Case #PB_Event_SizeWindow
ResizeGadget(#CNT_Header, 0,0, WindowWidth(#WIN_MAIN), 28)
ResizeGadget(#SP_MainBottom, 0,28, WindowWidth(#WIN_MAIN), WindowHeight(#WIN_MAIN)-28)
DrawAllBackgrounds()
Case #PB_Event_Gadget
If EventType() = #PB_EventType_Change
Select EventGadget()
Case #SP_LeftCenter, #SP_CenterRight, #SP_MainBottom
DrawAllBackgrounds()
EndSelect
EndIf
Case #PB_Event_Timer
If EventTimer() = #TMR_Play
currentFrame + 1
If currentFrame > frameEnd : currentFrame = frameStart : EndIf
DrawTimeline()
EndIf
EndSelect
ForEver
EndIf