Verteilungs "Gadget" für Screens

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Dostej
Beiträge: 529
Registriert: 01.10.2004 10:02
Kontaktdaten:

Verteilungs "Gadget" für Screens

Beitrag von Dostej »

So, ich möchte euch ein Stück Code vorstellen, das ich für mein Projektchen geschrieben habe.
Ich hoffe das der Name nicht zu exotisch gewählt ist.

Es geht darum, 100% auf verschiedene Blöcke aufzuteilen. Die Balkenlänge kann für ein "Gadget" mit der Maus gesetzt werden. Die jeweils anderen passen sich dann an. Man kann zwischen gleichmässiger oder proportionaler Anpassung wählen.

So, noch ein Bild:
Bild

Ich hoffe der Code ist ansonsten halbwegs leserlich (auch wenn sie manche über die vielen Falten wundern - mir hilfts den Überblick über den Code zu bewahren - ihr könnt ja die anderen löschen... :-) )

Würde mich ansonsten über Rückmeldungen freuen.

Code: Alles auswählen

; Distribution-gadgets
; coded 2007 by Dostej
; published under the LGPL

; What does it do
; You can create some grouped Progressbar-like "gadgets" for a screen enviroment
; Each group has together a max value of 100.
; If you change one gadget, the others of this group change their values, too


Structure VTGdgt
  Number_L.l
  Group_L.l
  X_Pos_L.l
  Y_Pos_L.l
  Width_L.l
  Height_L.l
  Value_L.l
  ImageNr_L.l
EndStructure
Global NewList DSTGadget.VTGdgt()


Global DSTGadget_Mode_GL.l = 1 ; mode 0: equal  Mode 1: proportional

Global MouseX_GL.l
Global MouseY_GL.l
Global MouseButton_GL.l



Procedure Free_DSTGadget(Number_L.l) ; free the gadget
  
  ForEach DSTGadget()
    If DSTGadget()\Number_L = Number_L
      DeleteElement(DSTGadget())
    EndIf
  Next
  
EndProcedure 

Procedure Create_DSTGadget(Number_L.l, Group_L.l, X_Pos_L.l, Y_Pos_L.l, Width_L.l, Height_L.l, ImageNr_L.l) ; create a new VT gadget
  ; free an other gadget with the same number
  Free_DSTGadget(Number_L)
  
  AddElement(DSTGadget())
  With DSTGadget()
    \Number_L = Number_L
    \Group_L = Group_L
    \X_Pos_L = X_Pos_L
    \Y_Pos_L = Y_Pos_L
    \Width_L = Width_L
    \Height_L = Height_L
    \Value_L = 0
    \ImageNr_L = ImageNr_L
  EndWith
  
EndProcedure 

Procedure Draw_DSTGadgets() ; Draw all DST-gadgets
  
  If StartDrawing(ScreenOutput())
    With DSTGadget()
      ForEach DSTGadget()
        DrawingMode(#PB_2DDrawing_Outlined)
        Box(\X_Pos_L, \Y_Pos_L, \Width_L, \Height_L, $FFFF00) ; #Col_Gadget_Text)
        
        ; draw the image depending on the value given
        w = ((\Width_L-2) * \Value_L) / 100
        If w > 0
          DrawImage(ImageID(\ImageNr_L), \X_Pos_L + 1, \Y_Pos_L + 1, w, \Height_L - 2)
        EndIf
      Next
    EndWith
    StopDrawing()
  EndIf
  
EndProcedure

Procedure Distribute_DSTGadgets(Group_L.l, Number_L.l, Diff_L.l) ; distribute the Difference to all gadgets but the "number_L" one
  a = 0
  With DSTGadget()
    ;{ count how many gadgets are in this group
    ForEach DSTGadget()
      If \Group_L = Group_L 
        If \Number_L <> Number_L
          a + 1
        Else
          Value = \Value_L
        EndIf
      EndIf
    Next
    a-1
    ;}
    
    If a > -1 ; if at least one other gadget
      ;{ shuffle all elements of these group in an array
      Dim Temp.POINT(a)
      Count_L = 0
      Sum_L = 0
      ForEach DSTGadget()
        If \Group_L = Group_L And \Number_L <> Number_L
          Temp(Count_L)\x = @DSTGadget()
          Temp(Count_L)\y = \Value_L
          Sum_L + \Value_L
          Count_L + 1
        EndIf
      Next
      ;}
      
      ;{ Distribute the difference
      ; equal
      If DSTGadget_Mode_GL = 1
        ; proportional
        If Sum_L > 0
          f.f = Diff_L / Sum_L
          For x = 0 To a
            Temp(x)\y + (Temp(x)\y * f)
          Next
        Else ; if all other values are 0 -> distribute equal
          Mode = 1
        EndIf
      EndIf
      
      If DSTGadget_Mode_GL = 0 Or Mode = 1
        Rest = Diff_L
        Repeat
          ; calc one share
          d = Int(Rest / (a+1))
          Rest = 0
          For x = 0 To a
            If Temp(x)\y >= d Or d > 0
              n = d
            Else 
              n = Temp(x)\y
            EndIf
            Temp(x)\y + n
            Rest + (d - n)
          Next
        Until Rest = 0
      EndIf
      ;}
      
      ;{ check for value of 100
      n = -100 + Value
      For x = 0 To a
        n + Temp(x)\y
      Next
      
      If n <> 0
        x = Random(a)
        Repeat
          If n > 0
            If Temp(x)\y > 0
              Temp(x)\y - 1
              n - 1
            EndIf
          Else
            If Temp(x)\y < 100
              Temp(x)\y + 1
              n + 1
            EndIf
          EndIf
          x + 1
          If x > a
            x = 0
          EndIf
        Until n = 0
      EndIf
      ;}
      
      ;{ wrote the values back to the LL
      For x = 0 To a
        ChangeCurrentElement(DSTGadget(), Temp(x)\x)
        \Value_L = Temp(x)\y
      Next
      ;}
    EndIf
  EndWith
  
EndProcedure 

Procedure.l GetValue_DSTGadget(Number_L.l) ; returns the value of the gadget
  Back_L.l
  
  ForEach DSTGadget()
    If DSTGadget()\Number_L = Number_L
      Back_L = DSTGadget()\Value_L
      Break
    EndIf
  Next
  
  ProcedureReturn Back_L
EndProcedure 
Procedure.l SetValue_DSTGadget(Number_L.l, Value_L.l) ; set the value of the gadget and change the other ones
  Back_L.l
  
  ForEach DSTGadget()
    If DSTGadget()\Number_L = Number_L
      If Value_L < 0
        Value_L = 0
      ElseIf Value_L > 100
        Value_L = 100
      EndIf
      Diff_L = DSTGadget()\Value_L - Value_L
      DSTGadget()\Value_L = Value_L
      Distribute_DSTGadgets(DSTGadget()\Group_L, Number_L, Diff_L)
      Break
    EndIf
  Next
  
  ProcedureReturn Back_L
EndProcedure 

Procedure.l DSTGadgetEvents() ; handle Mouseevents with the DSTGadgets, returns the number of the DSTGadget currently klicked
  Back_L.l = -1
  
  ; check which Gadget is clicked and set the new value
  With DSTGadget()
    ForEach DSTGadget()
      If MouseX_GL >= \X_Pos_L And MouseX_GL <= \X_Pos_L + \Width_L
        If MouseY_GL >= \Y_Pos_L And MouseY_GL <= \Y_Pos_L + \Height_L
          If MouseButton_GL ; if left or right button was pressed
            Back_L = \Number_L
            t = ((MouseX_GL - \X_Pos_L) * 100) / \Width_L
            Diff_L = \Value_L - t ; calc the difference
            \Value_L = t
            Group_L = \Group_L
            Number_L = \Number_L
            Break
          EndIf
        EndIf
      EndIf
    Next
    
    If Back_L <> -1  And Diff_L <> 0 ; a gadget was klicked - set the other values
      Distribute_DSTGadgets(Group_L, Number_L, Diff_L)
    EndIf
  EndWith
  
  ProcedureReturn Back_L 
EndProcedure 


If InitSprite() = 0 Or InitKeyboard() = 0 Or InitMouse()= 0
  MessageRequester("Fehler", "init") 
  End 
EndIf

If OpenWindow(1, 0, 0, 1024, 768, "", #PB_Window_BorderLess)
  If OpenWindowedScreen(WindowID(1), 0, 0, 1024, 768, 0, 0, 0)
    ; create a mouse
    If CreateSprite(0, 8, 8) 
      If StartDrawing(SpriteOutput(0)) 
        Circle(4, 4, 4, $FFFF80) 
        StopDrawing()
      EndIf 
    EndIf
    
    If CreateImage(1, 198, 18)
      If StartDrawing(ImageOutput(1))
        Box(0, 0, 198, 18, $0080FF)
        StopDrawing()
      EndIf
    EndIf
    
    Create_DSTGadget(1, 1, 100, 200, 200, 20, 1)
    Create_DSTGadget(2, 1, 100, 230, 200, 20, 1)
    Create_DSTGadget(3, 1, 100, 260, 200, 20, 1)
    Create_DSTGadget(4, 1, 100, 290, 200, 20, 1)
    
    ForEach DSTGadget()
      DSTGadget()\Value_L = 25
    Next
    
    
    ;{ Eventloop
    Repeat ; Start of the event loop
      ClearScreen(0)
      
      ExamineMouse()
      MouseX_GL = MouseX()
      MouseY_GL = MouseY()
      MouseButton_GL = MouseButton(1) | MouseButton(2)<<1
      
      GadgetClicked = DSTGadgetEvents() ; handles the gadgets
      If GadgetClicked > -1
        ; show all values
        Debug "used gadget: " + Str(GadgetClicked) + "    new values"
        ForEach DSTGadget()
          Debug DSTGadget()\Value_L
        Next
      EndIf
      Draw_DSTGadgets()
      
      DisplaySprite(0, MouseX_GL, MouseY_GL)
      
      FlipBuffers(0)
      
      
      Delay(30)
      
      ExamineMouse()
      
    Until Event = #PB_Event_CloseWindow Or MouseButton(2) ; End of the event loop
    ;}
  EndIf
EndIf


Benutzeravatar
PureLust
Beiträge: 1145
Registriert: 21.07.2005 00:02
Computerausstattung: Hab aktuell im Grunde nur noch 'nen Lenovo Yoga 2 Pro im Einsatz.
Wohnort: am schönen Niederrhein

Beitrag von PureLust »

Nach meiner Auffassung solltest Du einen Balken den man angeklickt hat solange aktiv halten bis man die Maustaste wieder losgelassen hat.

An sonsten aber sehr nett. :allright:
[Dynamic-Dialogs] - komplexe dynamische GUIs einfach erstellen
[DeFlicker] - Fenster flimmerfrei resizen
[WinFX] - Window Effekte (inkl. 'durchklickbares' Window)
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Beitrag von AND51 »

Nette Idee, aber gerade der equal-Modus funktioneirt nicht, wenn ich nur einen Balken verändere müssen doch die anderen gleich lang bleiben... tun sie aber nicht, sie haben nach mehrmaligen vor- und zurückschieben des einen Balkens immer eine unterschiedliche Länge.
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Benutzeravatar
Dostej
Beiträge: 529
Registriert: 01.10.2004 10:02
Kontaktdaten:

Beitrag von Dostej »

@AND51: Ja, habe ich auch bemerkt. Da liegt daran, das ich die "Reste", z.B. 2, bei drei Gadgets, per Zufall verteile. Daher kommen mit der Zeit die Ungleichheiten.

Ich habe schon überlegt, wie ich das ändern könnte, aber was so recht schlaues ist mir nicht eingefallen. Mit floats hats das Problem, das es vermutlich auch nicht genau auf 100 aufgeht. :?


Ich habe die Funktionen noch etwas erweitert, so das man Gadgets auch sperren kann.

Code: Alles auswählen

; Distribution-gadgets
; coded 2007 by Dostej
; published under the LGPL

; What does it do
; You can create some grouped Progressbar-like "gadgets" for a screen enviroment
; Each group has together a max value of 100.
; If you change one gadget, the others of this group change their values, too


Structure VTGdgt
  Number_L.l
  Group_L.l
  X_Pos_L.l
  Y_Pos_L.l
  Width_L.l
  Height_L.l
  Value_L.l
  Lock_L.l
  ImageNr_L.l
EndStructure
Global NewList DSTGadget.VTGdgt()


Global DSTGadget_Mode_GL.l = 1 ; mode 0: equal  Mode 1: proportional

Global MouseX_GL.l
Global MouseY_GL.l
Global MouseButton_GL.l



Procedure Free_DSTGadget(Number_L.l) ; free the gadget
  
  ForEach DSTGadget()
    If DSTGadget()\Number_L = Number_L
      DeleteElement(DSTGadget())
    EndIf
  Next
  
EndProcedure 

Procedure Create_DSTGadget(Number_L.l, Group_L.l, X_Pos_L.l, Y_Pos_L.l, Width_L.l, Height_L.l, ImageNr_L.l, Value_L.l) ; create a new VT gadget
  ; free an other gadget with the same number
  Free_DSTGadget(Number_L)
  
  AddElement(DSTGadget())
  With DSTGadget()
    \Number_L = Number_L
    \Group_L = Group_L
    \X_Pos_L = X_Pos_L
    \Y_Pos_L = Y_Pos_L
    \Width_L = Width_L
    \Height_L = Height_L
    \Value_L = Value_L
    \Lock_L = 0
    \ImageNr_L = ImageNr_L
  EndWith
  
EndProcedure 

Procedure Draw_DSTGadgets() ; Draw all DST-gadgets
  
  If StartDrawing(ScreenOutput())
    With DSTGadget()
      ForEach DSTGadget()
        DrawingMode(#PB_2DDrawing_Outlined)
        If \Lock_L
          Box(\X_Pos_L, \Y_Pos_L, \Width_L, \Height_L, $FFFF00) ; #Col_Gadget_Text)
        Else
          Box(\X_Pos_L, \Y_Pos_L, \Width_L, \Height_L, $D949E9) ; #Col_Gadget_Text)
        EndIf
        
        ; draw the image depending on the value given
        w = ((\Width_L-2) * \Value_L) / 100
        If w > 0
          DrawImage(ImageID(\ImageNr_L), \X_Pos_L + 1, \Y_Pos_L + 1, w, \Height_L - 2)
        EndIf
      Next
    EndWith
    StopDrawing()
  EndIf
  
EndProcedure

Procedure Distribute_DSTGadgets(Group_L.l, Number_L.l, Diff_L.l) ; distribute the Difference to all gadgets but the "number_L" one
  a = 0
  Lock_L.l
  
  With DSTGadget()
    ;{ count how many gadgets are in this group and maybe corr Diff_L
    ForEach DSTGadget()
      If \Group_L = Group_L 
        If \Lock_L = 0
          If \Number_L <> Number_L
            a + 1
          Else
            Value = \Value_L
          EndIf
        Else
          Lock_L = \Value_L
        EndIf
      EndIf
    Next
    a-1
    
    ; maybe corr Diff_L
    If Diff_L > 0
      If a = -1 ; no unlocked gadget
        Diff_L = 0
      EndIf
    Else
      If 100 - Lock_L - Value + Diff_L < 0
        ForEach DSTGadget()
          If \Group_L = Group_L 
            If \Number_L = Number_L
              \Value_L = 100 - Lock_L
              Diff_L = 0
              a = -1
            Else
              If \Lock_L = 0
                \Value_L = 0
              EndIf
            EndIf
          EndIf
        Next
      EndIf
    EndIf
    ;}
    
    If a > -1 ; if at least one other gadget
      ;{ shuffle all elements of these group in an array
      Dim Temp.POINT(a)
      Count_L = 0
      Sum_L = 0
      ForEach DSTGadget()
        If \Group_L = Group_L And \Number_L <> Number_L And \Lock_L = 0
          Temp(Count_L)\x = @DSTGadget()
          Temp(Count_L)\y = \Value_L
          Sum_L + \Value_L
          Count_L + 1
        EndIf
      Next
      ;}
      
      ;{ Distribute the difference
      ; equal
      If DSTGadget_Mode_GL = 1
        ; proportional
        If Sum_L > 0
          f.f = Diff_L / Sum_L
          For x = 0 To a
            Temp(x)\y + (Temp(x)\y * f)
          Next
        Else ; if all other values are 0 -> distribute equal
          Mode = 1
        EndIf
      EndIf
      
      If DSTGadget_Mode_GL = 0 Or Mode = 1
        Rest = Diff_L
        Repeat
          ; calc one share
          
          d = Int(Rest / (a+1))
          Rest = 0
          For x = 0 To a
            n = 0
            If d < 0 ; some value to REDUCE
              If Temp(x)\y + d >= 0
                n = d
              Else 
                n = Temp(x)\y
              EndIf
              
            Else ; d > 0 ; some value to ADD
              If Temp(x)\y + d >= 100 ; would exceed the limit
                n = 100 - Temp(x)\y
              Else 
                n = d
              EndIf
            EndIf
            Temp(x)\y + n
            Rest + (d - n)
          Next
        Until Rest = 0
      EndIf
      
      ;}
      ;{ check for value of 100
      n = -100 + Value + Lock_L
      For x = 0 To a
        n + Temp(x)\y
      Next
      
      If n <> 0
        x = Random(a)
        Repeat
          If n > 0
            If Temp(x)\y > 0
              Temp(x)\y - 1
              n - 1
            EndIf
          Else
            If Temp(x)\y < 100
              Temp(x)\y + 1
              n + 1
            EndIf
          EndIf
          x + 1
          If x > a
            x = 0
          EndIf
        Until n = 0
        
        For x = 0 To a
          Debug Temp(x)\y
          If Temp(x)\y < 0
            CallDebugger
          ElseIf Temp(x)\y > 100
            CallDebugger
          EndIf
        Next
      EndIf
      ;}
      
      ;{ wrote the values back to the LL
      For x = 0 To a
        ChangeCurrentElement(DSTGadget(), Temp(x)\x)
        \Value_L = Temp(x)\y
      Next
      ;}
    EndIf
  EndWith
  
EndProcedure 

Procedure.l GetValue_DSTGadget(Number_L.l) ; returns the value of the gadget
  Back_L.l
  
  ForEach DSTGadget()
    If DSTGadget()\Number_L = Number_L
      Back_L = DSTGadget()\Value_L
      Break
    EndIf
  Next
  
  ProcedureReturn Back_L
EndProcedure 
Procedure.l SetValue_DSTGadget(Number_L.l, Value_L.l) ; set the value of the gadget and change the other ones
  Back_L.l
  
  ForEach DSTGadget()
    If DSTGadget()\Number_L = Number_L
      If Value_L < 0
        Value_L = 0
      ElseIf Value_L > 100
        Value_L = 100
      EndIf
      Diff_L = DSTGadget()\Value_L - Value_L
      DSTGadget()\Value_L = Value_L
      Distribute_DSTGadgets(DSTGadget()\Group_L, Number_L, Diff_L)
      Break
    EndIf
  Next
  
  ProcedureReturn Back_L
EndProcedure 

Procedure.l DSTGadgetEvents() ; handle Mouseevents with the DSTGadgets, returns the number of the DSTGadget currently klicked
  Back_L.l = -1
  
  ; check which Gadget is clicked and set the new value
  With DSTGadget()
    ForEach DSTGadget()
      If \Lock_L = 0 ; if not locked
        If MouseX_GL >= \X_Pos_L And MouseX_GL <= \X_Pos_L + \Width_L
          If MouseY_GL >= \Y_Pos_L And MouseY_GL <= \Y_Pos_L + \Height_L
            If MouseButton_GL ; if left or right button was pressed
              Back_L = \Number_L
              t = ((MouseX_GL - \X_Pos_L) * 100) / \Width_L
              Diff_L = \Value_L - t ; calc the difference
              \Value_L = t
              Group_L = \Group_L
              Number_L = \Number_L
              Break
            EndIf
          EndIf
        EndIf
      EndIf
    Next
    
    If Back_L <> -1  And Diff_L <> 0 ; a gadget was klicked - set the other values
      Distribute_DSTGadgets(Group_L, Number_L, Diff_L)
    EndIf
  EndWith
  
  ProcedureReturn Back_L 
EndProcedure 

Procedure Lock_DSTGadget(Number_L.l, Mode_L.l) ; (un-)lock a gadget, Mode 0: unlock      Mode 1: lock
  ForEach DSTGadget()
    If DSTGadget()\Number_L = Number_L
      DSTGadget()\Lock_L = Mode_L
    EndIf
  Next
EndProcedure 


If InitSprite() = 0 Or InitKeyboard() = 0 Or InitMouse()= 0
  MessageRequester("Fehler", "init") 
  End 
EndIf

If OpenWindow(1, 0, 0, 1024, 768, "", #PB_Window_BorderLess)
  If OpenWindowedScreen(WindowID(1), 0, 0, 1024, 768, 0, 0, 0)
    ; create a mouse
    If CreateSprite(0, 8, 8) 
      If StartDrawing(SpriteOutput(0)) 
        Circle(4, 4, 4, $FFFF80) 
        StopDrawing()
      EndIf 
    EndIf
    
    If CreateImage(1, 198, 18)
      If StartDrawing(ImageOutput(1))
        Box(0, 0, 198, 18, $0080FF)
        StopDrawing()
      EndIf
    EndIf
    
    Create_DSTGadget(1, 1, 50, 200, 200, 20, 1, 40)
    Create_DSTGadget(2, 1, 300, 230, 200, 20, 1, 40)
    Create_DSTGadget(3, 1, 550, 260, 200, 20, 1, 10)
    Create_DSTGadget(4, 1, 800, 290, 200, 20, 1, 10)
    
    Lock_DSTGadget(3, 1)
    
    ForEach DSTGadget()
      DSTGadget()\Value_L = 25
    Next
    
    
    ;{ Eventloop
    Repeat ; Start of the event loop
      ClearScreen(0)
      
      ExamineMouse()
      MouseX_GL = MouseX()
      MouseY_GL = MouseY()
      MouseButton_GL = MouseButton(1) | MouseButton(2)<<1
      
      GadgetClicked = DSTGadgetEvents() ; handles the gadgets
      If GadgetClicked > -1
        ; show all values
        Debug "used gadget: " + Str(GadgetClicked) + "    new values"
        ForEach DSTGadget()
          Debug DSTGadget()\Value_L
        Next
      EndIf
      Draw_DSTGadgets()
      
      DisplaySprite(0, MouseX_GL, MouseY_GL)
      
      FlipBuffers(0)
      
      
      Delay(30)
      
      
    Until Event = #PB_Event_CloseWindow Or MouseButton(2) ; End of the event loop
    ;}
  EndIf
EndIf


 
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

rechne doch mit fixcomma.
also in integer aber in 1000stel statt mit ganzen einheiten.
dein schieber zeigt also 0-100 an, aber seine werte laufen von 0-100000
das würde dir schon ein bißchen mehr möglichkeiten geben.

allerdings wäre es in deinem rahmen mit Double eigentlich ausreichend...
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Antworten