1 mesaj
  • Mesaje: 7
  • Reacții: 6
  • Mesaje utile: 0
  • Status: Pierd vremea ^.^
  • Server: Testam si invatam :D
  • Regat: Jinno
  • [ID Discord]
  • Contact:

    Medalii

    *Descriere: GM Buttons – Whisper UI (Varianta personalizată)

    Am găsit pe forum o versiune mai veche de GM Buttons pentru Whisper și am pornit de la ideea aceea. Varianta originală era destul de simplă: nu exista board, butoanele erau puse manual direct sub numele targetului, fără centrare, fără aranjare automată și fără logică la resize. Totul era setat „din pix”, poziții fixe.
    În plus, uiwhisper.py depindea direct de whisperdialog.py pentru imagini, distanțe, tipul butoanelor și text, ceea ce făcea personalizarea destul de limitată – nu prea aveai libertate să te joci cu layout-ul sau să adaugi lucruri noi fără să strici altceva.

    Am folosit acea variantă doar ca punct de plecare și am construit o versiune proprie, mult mai flexibilă.

    Ce aduce nou codul:

    Board dedicat pentru butoanele GM (ThinBoardGold), separat de Whisper

    Aranjare automată și centrare a butoanelor pe rânduri

    Padding, spacing și dimensiuni ușor de modificat

    Pickable area corectă, care include tot ce ține de GM board

    Comportament stabil la resize (fără glitch-uri vizuale)

    GM board afișat doar pentru GM și doar când există target

    Codul nu mai depinde de whisperdialog.py pentru imagini, poziții sau tipuri de butoane

    Poți adăuga ușor butoane noi, schimba culori, layout, spacing etc.

    Logica este simplă: momentan butoanele trimit comenzi direct în chat / consolă (atât), dar sistemul e gândit să poată fi extins

    Practic, am trecut de la niște butoane puse „aruncate” sub targetname, la un mic sistem organizat, controlabil și mult mai ușor de modificat.

    Mică mențiune 🙂

    Nu sunt expert în cod. Încă învăț și am avut ajutor de la AI pe parcursul dezvoltării. Încep să prind din ce în ce mai bine lucrurile, dar nu e ușor și mai durează, așa că… nu mă înjurați 😄
    Important e că funcționează, e stabil și e gândit să fie ușor de extins pe viitor.

    uiwhisper.py
    Code:
    ###############################################################
    #  GM BOARD BUTTONS SYSTEM – VECHI
    #
    #  Autor logică & concept:
    #    Vaduva Liviu Bogdan (l3oogie)
    #
    #  Implementare tehnică:
    #    realizată anterior (versiune veche), fără scroll și toggle bar
    #
    # -------------------------------------------------------------
    #  DESCRIERE GENERALĂ
    # -------------------------------------------------------------
    #
    #  Acest fișier conține versiunea veche a sistemului de GM Board + butoane.
    #  Include:
    #  - Board vizual pentru butoanele GM (ThinBoardGold)
    #  - Butoane GM aranjate pe rânduri și centrate automat
    #  - Padding-uri și spacing configurabile
    #  - Pickable area complet funcțională, acoperind toate butoanele
    #
    #  IMPORTANT:
    #  - Nu există scroll vertical sau container de clipping.
    #  - Nu există bara GM cu toggle.
    #  - Butoanele sunt afișate direct pe board, fără gestionare avansată a vizibilității.
    #
    # -------------------------------------------------------------
    #  LAYOUT DINAMIC
    # -------------------------------------------------------------
    #
    #  - Butoanele GM sunt aranjate automat pe rânduri, centrate în board.
    #  - Lățimea board-ului se calculează din dimensiunea ferestrei (fix sau adaptivă).
    #  - Dacă butoanele depășesc lățimea board-ului, acestea trec pe rândul următor.
    #  - Înălțimea board-ului se ajustează automat între min și max în funcție de butoane.
    #
    # -------------------------------------------------------------
    #  PICKABLE AREA
    # -------------------------------------------------------------
    #
    #  - Pickable area acoperă complet butoanele GM.
    #  - Se ajustează dinamic la dimensiunea board-ului și poziția butoanelor.
    #  - Asigură că toate click-urile sunt detectate corect pe butoanele GM.
    #
    # -------------------------------------------------------------
    #  INTEGRITATE ȘI UTILIZARE
    # -------------------------------------------------------------
    #
    #  - Codul gestionează exclusiv butoanele GM și layout-ul lor.
    #  - Funcționalitatea GM și permisiunile sunt gestionate server-side.
    #  - Poți adăuga sau elimina butoane fără să modifici logica de aranjare.
    #
    # -------------------------------------------------------------
    #  OBSERVAȚII
    # -------------------------------------------------------------
    #
    #  - Layout dinamic și pickable area completă sunt principalele caracteristici.
    #  - Lipsesc scroll, clipping și bara toggle (prezentă în versiunea nouă).
    #  - Aspectul vizual poate fi ajustat fără a afecta funcționalitatea.
    #
    ###############################################################
    
    
    import ui          # Module pentru UI: ferestre, butoane, scrollbars
    import net         # Modul pentru trimitere comenzi către server (ex: /comanda joc gm1, /comanda joc gm1)
    import chat        # Modul pentru logica chat (afișarea whisper-urilor)
    import player      # Modul pentru informații despre jucătorul curent
    import app         # Funcții generale ale aplicației (cursor, timp, resize)
    import localeInfo  # Localizare: verifică setările limbii, RTL etc.
    import ime         # Input Method Editor, pentru input text complex
    import chr         # Module pentru verificări caractere / GM (Game Master)
    
    # =======================================
    # CLASA WHISPER BUTTON
    # =======================================
    class WhisperButton(ui.Button):
    	def __init__(self):
    		ui.Button.__init__(self, "TOP_MOST")  # Inițializează butonul în stratul Top Most
    
    	def __del__(self):
    		ui.Button.__del__(self)  # Destructor pentru eliberarea memoriei
    
    	def SetToolTipText(self, text, x=0, y=32):
    		ui.Button.SetToolTipText(self, text, x, y)  # Setează text standard tooltip
    		self.ToolTipText.Show()                      # Afișează tooltip-ul
    
    	def SetToolTipTextWithColor(self, text, color, x=0, y=32):
    		ui.Button.SetToolTipText(self, text, x, y)  # Setează text tooltip
    		self.ToolTipText.SetPackedFontColor(color)  # Aplică culoare font
    		self.ToolTipText.Show()                     # Afișează tooltip-ul colorat
    
    	def ShowToolTip(self):
    		if 0 != self.ToolTipText:
    			self.ToolTipText.Show()  # Forțează afișarea tooltip-ului dacă există
    
    	def HideToolTip(self):
    		if 0 != self.ToolTipText:
    			self.ToolTipText.Hide()  # Ascunde tooltip-ul dacă există
    
    
    # =======================================
    # CLASA GM BUTTON LIBRARY
    # =======================================
    class GMButtonLibrary:
    	def __init__(self):
    		self.groups = {}  # Dicționar pentru grupuri de butoane GM
    
    	def RegisterGroupWithColors(self, group_name, buttons_with_colors):
    		self.groups[group_name] = {}  # Creează grupul dacă nu există
    		for name, (btn, color) in buttons_with_colors.items():
    			self.groups[group_name][name] = btn  # Adaugă butonul în grup
    			if btn and hasattr(btn, "SetTextColor"):  # Verifică dacă butonul suportă SetTextColor
    				btn.SetTextColor(color)  # Setează culoarea textului butonului
    
    	def ShowGroup(self, group_name):
    		group = self.groups.get(group_name)  # Obține grupul de butoane
    		if not group:
    			return  # Dacă nu există grupul, nu face nimic
    		for btn in group.values():
    			if btn:
    				btn.Show()  # Afișează fiecare buton din grup
    
    	def HideGroup(self, group_name):
    		group = self.groups.get(group_name)
    		if not group:
    			return  # Dacă nu există grupul, nu face nimic
    		for btn in group.values():
    			if btn:
    				btn.Hide()  # Ascunde fiecare buton din grup
    
    
    # =======================================
    # CLASA WHISPER DIALOG
    # =======================================
    class WhisperDialog(ui.ScriptWindow):
    
    	def IgnoreTarget(self):
    		net.SendChatPacket("/ignore " + self.targetName)
    
    	def ReportViolentWhisper(self):
    		net.SendChatPacket("/reportviolentwhisper " + self.targetName)
    
    
    	# =======================================
    	# Text Renderer intern pentru whisper
    	# =======================================
    	class TextRenderer(ui.Window):
    		def SetTargetName(self, targetName):
    			self.targetName = targetName  # Setează numele destinatarului whisper-ului
    
    		def OnRender(self):
    			(x, y) = self.GetGlobalPosition()           # Obține poziția globală a ferestrei
    			chat.RenderWhisper(self.targetName, x, y)  # Desenează whisper-ul pe ecran
    
    	# =======================================
    	# Buton de resize
    	# =======================================
    	class ResizeButton(ui.DragButton):
    		def __init__(self):
    			ui.DragButton.__init__(self)  # Inițializează butonul drag
    
    		def __del__(self):
    			ui.DragButton.__del__(self)   # Destructor
    
    		def OnMouseOverIn(self):
    			app.SetCursor(app.HVSIZE)  # Cursorul se schimbă pentru resize
    
    		def OnMouseOverOut(self):
    			app.SetCursor(app.NORMAL)   # Cursorul revine la normal
    
    	# =======================================
    	# INIT FEREASTRA WHISPER
    	# =======================================
    	def __init__(self, eventMinimize, eventClose):
    		ui.ScriptWindow.__init__(self)  # Inițializează ScriptWindow
    		self.targetName = ""            # Numele destinatarului curent
    		self.eventMinimize = eventMinimize  # Funcție de minimizare
    		self.eventClose = eventClose        # Funcție de închidere
    		self.eventAcceptTarget = None       # Funcție pentru accept target
    
    		self.gm_button_library = GMButtonLibrary()  # Gestionare butoane GM
    
    	def __del__(self):
    		ui.ScriptWindow.__del__(self)  # Destructor ScriptWindow
    
    	# =======================================
    	# LOAD DIALOG
    	# =======================================
    	def LoadDialog(self):
    		try:
    			pyScrLoader = ui.PythonScriptLoader()  # Loader pentru scriptul UI
    			pyScrLoader.LoadScriptFile(self, "UIScript/WhisperDialog.py")  # Încarcă scriptul
    		except:
    			import exception
    			exception.Abort("WhisperDialog.LoadDialog.LoadScript")  # Abort dacă fișierul nu există
    
    		try:
    			GetObject = self.GetChild  # Shortcut pentru GetChild
    			self.titleName = GetObject("titlename")               # Titlul ferestrei
    			self.titleNameEdit = GetObject("titlename_edit")      # Input pentru nume
    			self.closeButton = GetObject("closebutton")           # Buton de închidere
    			self.scrollBar = GetObject("scrollbar")               # Scroll bar
    			self.chatLine = GetObject("chatline")                 # Linie de chat
    			self.minimizeButton = GetObject("minimizebutton")     # Buton minimize
    			self.sendButton = GetObject("sendbutton")             # Buton trimite
    			self.acceptButton = GetObject("acceptbutton")         # Buton accept target
    			self.board = GetObject("board")                       # Board principal
    			self.editBar = GetObject("editbar")                   # Bara editare text
    			self.gamemasterMark = GetObject("gamemastermark")     # GM mark vizual
    			self.ignoreButton = GetObject("ignorebutton")
    			self.reportViolentWhisperButton = GetObject("reportviolentwhisperbutton")
    			
    		except:
    			import exception
    			exception.Abort("DialogWindow.LoadDialog.BindObject")  # Abort dacă element lipsă
    
    		# =======================================
    		# Ascundem elemente inițiale
    		# =======================================
    		self.gamemasterMark.Hide()     # Ascunde mark GM
    		self.titleName.SetText("")     # Curăță titlu
    		self.titleNameEdit.SetText("") # Curăță input
    
    		# =======================================
    		# Setăm evenimentele principale
    		# =======================================
    		self.minimizeButton.SetEvent(ui.__mem_func__(self.Minimize))  # Eveniment minimize
    		self.closeButton.SetEvent(ui.__mem_func__(self.Close))        # Eveniment close
    		self.scrollBar.SetPos(1.0)                                     # Set scroll la final
    		self.scrollBar.SetScrollEvent(ui.__mem_func__(self.OnScroll))  # Scroll event
    		self.chatLine.SetReturnEvent(ui.__mem_func__(self.SendWhisper))  # Enter trimite
    		self.chatLine.SetEscapeEvent(ui.__mem_func__(self.Minimize))     # Escape minimize
    		self.chatLine.SetMultiLine()                                     # Multiline input
    		self.sendButton.SetEvent(ui.__mem_func__(self.SendWhisper))       # Buton trimite
    		self.titleNameEdit.SetReturnEvent(ui.__mem_func__(self.AcceptTarget)) # Enter accept
    		self.titleNameEdit.SetEscapeEvent(ui.__mem_func__(self.Close))         # Escape close
    		self.acceptButton.SetEvent(ui.__mem_func__(self.AcceptTarget))         # Accept target
    		self.ignoreButton.SetToggleDownEvent(ui.__mem_func__(self.IgnoreTarget))
    		self.ignoreButton.SetToggleUpEvent(ui.__mem_func__(self.IgnoreTarget))
    		self.reportViolentWhisperButton.SetEvent(ui.__mem_func__(self.ReportViolentWhisper))
    
    
    		# =======================================
    		# TextRenderer
    		# =======================================
    		self.textRenderer = self.TextRenderer()  # Creează renderer text
    		self.textRenderer.SetParent(self)        # Set parent
    		self.textRenderer.SetPosition(20, 28)    # Poziție pe fereastră
    		self.textRenderer.SetTargetName("")      # Target gol inițial
    		self.textRenderer.Show()                 # Afișează renderer
    
    		# =======================================
    		# Resize Button
    		# =======================================
    		self.resizeButton = self.ResizeButton()              # Creează buton resize
    		self.resizeButton.SetParent(self)                    # Set parent
    		self.resizeButton.SetSize(20, 20)                    # Dimensiune resize button
    		self.resizeButton.SetPosition(280, 180)             # Poziție inițială
    		self.resizeButton.SetMoveEvent(ui.__mem_func__(self.ResizeWhisperDialog)) # Event la drag
    		self.resizeButton.Show()                             # Afișează butonul
    
    
    		# =======================================
    		# GM Buttons Board setup (cu randuri si centrare)
    		# =======================================
    
    		# ===============================
    		# SETĂRI BOARD GM FAȚĂ DE WHISPER
    		# ===============================
    
    		self.gm_buttons_board_min_height = 25  
    		# => Înălțimea minimă a board-ului GM (nu micșorează mai jos de atât)
    		# Modifică doar dimensiunea board-ului, nu afectează butoanele.
    
    		self.gm_board_padding_top = 23    # => Distanța board-ului GM față de Whisper în sus (sus board față de bordură / fereastră Whisper)
    		self.gm_board_padding_bottom = 23 # => Distanța board-ului GM față de bordură jos pe inaltime 
    		self.gm_board_padding_left = 10   # => Distanța board-ului GM față de stânga ferestrei Whisper
    		self.gm_board_padding_right = 10  # => Distanța board-ului GM față de dreapta ferestrei Whisper
    		self.gm_button_padding_left = 15   # distanța butoanelor față de stânga board GM
    		self.gm_button_padding_right = 15  # distanța butoanelor față de dreapta board GM
    
    
    		self.gm_buttons_distance_from_whisper = 3  
    		# => Spațiul dintre Whisper (partea de jos a ferestrei) și bordul GM
    		# Toate acestea afectează **doar poziția și dimensiunea board-ului GM** față de Whisper
    		# Nu modifică spațiul dintre butoane.
    
    		# ===============================
    		# SETĂRI BUTOANE GM FAȚĂ DE BOARD GM
    		# ===============================
    
    		self.gm_button_spacing_horizontal = 2  
    		# => Spațiul între butoanele de pe același rând (orizontal)
    		self.gm_button_spacing_vertical = 4    
    		# => Spațiul între rânduri de butoane (vertical)
    
    		# Observație: acestea afectează doar **butoanele GM în interiorul board-ului**, 
    		# nu schimbă dimensiunea sau poziția board-ului față de Whisper.
    
    		# =======================================
    		# Creare board GM
    		# =======================================
    
    		self.gm_buttons_board = ui.ThinBoardGold()  # creează board-ul vizual GM
    		self.gm_buttons_board.SetParent(self)       # board-ul este copil al WhisperDialog
    
    		# Setăm dimensiune inițială (înălțime minimă, lățime adaptată la Whisper)
    		self.gm_buttons_board.SetSize(
    			self.GetWidth() - (self.gm_board_padding_left + self.gm_board_padding_right),  
    			# => lățimea board-ului = lățimea Whisper - padding stânga/dreapta
    			self.gm_buttons_board_min_height  
    			# => înălțimea minimă setată mai sus
    		)
    
    		# Poziționare inițială board GM
    		self.gm_buttons_board.SetPosition(
    			self.gm_board_padding_left,  # => stânga board = padding stânga față de Whisper
    			self.GetHeight() + self.gm_buttons_distance_from_whisper + self.gm_board_padding_top
    			# => jos board = înălțimea Whisper + distanță față de Whisper + padding sus board
    		)
    		self.gm_buttons_board.Hide()  # board-ul GM rămâne ascuns până când GM deschide fereastra
    
    
    
    
    		# =======================================
    		# Creare GM Test Buttons
    		# =======================================
    		self.__CreateGMTestButtons()      # Creează butoanele de test
    		self.__UpdateGMBoardLayout()      # Aranjează butoanele pe board
    
    		# =======================================
    		# Resize Whisper Dialog
    		# =======================================
    		self.ResizeWhisperDialog()        # Aplica resize inițial
    
    
    	# =======================================
    	# Destroy complet
    	# =======================================
    	def Destroy(self):
    		self.eventMinimize = None      # Șterge referința la funcția de minimize
    		self.eventClose = None         # Șterge referința la funcția de close
    		self.eventAcceptTarget = None  # Șterge referința la funcția de accept target
    
    		self.ClearDictionary()         # Curăță toate referințele interne de UI
    		self.scrollBar.Destroy()       # Test1 scrollBar-ul
    		self.titleName = None          # Șterge referința la titlu
    		self.titleNameEdit = None      # Șterge referința la input
    		self.closeButton = None        # Șterge referința la buton close
    		self.scrollBar = None          # Șterge referința la scrollBar
    		self.chatLine = None           # Șterge referința la chatLine
    		self.sendButton = None         # Șterge referința la sendButton
    		self.ignoreButton = None      # Dezactivat, nu există
    		self.reportViolentWhisperButton = None  # Dezactivat, nu există
    		self.acceptButton = None       # Șterge referința la acceptButton
    		self.minimizeButton = None     # Șterge referința la minimizeButton
    		self.textRenderer = None       # Șterge referința la textRenderer
    		self.board = None              # Șterge referința la board principal
    		self.editBar = None            # Șterge referința la editBar
    		self.resizeButton = None       # Șterge referința la resizeButton
    		self.gm_buttons_board = None   # Șterge referința la GM board
    		self.gm_button_library = None  # Șterge referința la biblioteca de butoane GM
    
    	# =======================================
    	# Creare butoane GM Test
    	# =======================================
    	def __CreateGMTestButtons(self):
    		self.gm_test_buttons = []  # Listă pentru toate butoanele GM
    
    		# Lista de nume pentru butoanele GM
    		button_names = ["Warp", "Transfer", "Kill", "Kick",
    						"Test1", "Test2", "Test3:", "Test4::",
    						"Test5:", "Test6:", "Test7:", "Test8:"]
    
    		# Dicționar comenzi per buton
    		commands_per_button = {
    			"Warp": lambda: net.SendChatPacket("/warp " + self.targetName),      # Warp target
    			"Transfer": lambda: net.SendChatPacket("/Transfer " + self.targetName), # Transfer target
    			"Kill": lambda: net.SendChatPacket("/kill " + self.targetName),       # Kill target
    			"Kick": lambda: net.SendChatPacket("/dc " + self.targetName),         # Kick target
    			"Test1": lambda: net.SendChatPacket("Test1 " + self.targetName), # Test1 target
    			"Test2": lambda: net.SendChatPacket("Test2 " + self.targetName),     # Test2 target
    			"Test3:": lambda: net.SendChatPacket("Test3: " + self.targetName),       # Test3: target
    			"Test4::": lambda: net.SendChatPacket("Test4:: " + self.targetName),   # Test4:: target
    			"Test5:": lambda: net.SendChatPacket("Test5: " + self.targetName),   # Test5: target
    			"Test6:": lambda: net.SendChatPacket("Test6: " + self.targetName),       # Test6: target
    			"Test7:": lambda: net.SendChatPacket("Test7: " + self.targetName), # Set level target
    			"Test8:": lambda: net.SendChatPacket("Test8: " + self.targetName)   # Set gold target
    		}
    
    		# Culoare butoane per nume
    		colors_per_button = {
    			"Warp": 0xccffffff, "Transfer": 0xcc1f4ad5, "Kill": 0xccf3c756, "Kick": 0xccf5273f,
    			"Test1": 0xccffffff, "Test2": 0xccffffff, "Test3:": 0xccffffff, "Test4::": 0xccffffff,
    			"Test5:": 0xccffffff, "Test6:": 0xccffffff, "Test7:": 0xccffffff, "Test8:": 0xccffffff
    		}
    
    		# Creare butoane efective
    		for name in button_names:
    			btn = ui.Button()                     # Creează un nou buton
    			btn.SetParent(self.gm_buttons_board) # Setează board ca parent
    			# Setează vizualurile pentru diferite stări
    			btn.SetUpVisual("d:/ymir work/ui/public/Large_Button_01.sub")
    			btn.SetOverVisual("d:/ymir work/ui/public/Large_Button_02.sub")
    			btn.SetDownVisual("d:/ymir work/ui/public/Large_Button_03.sub")
    			btn.SetText(name)                     # Setează textul butonului
    			btn.test_name = name                  # Nume test pentru debug
    
    			if name in commands_per_button:
    				btn.SetEvent(commands_per_button[name])  # Asociază comanda la click
    
    			btn.Show()                             # Afișează butonul
    			self.gm_test_buttons.append(btn)       # Adaugă în lista GM
    			setattr(self, name, btn)               # Creează atribut dinamic pentru fiecare buton
    
    		# Înregistrează grupul de butoane cu culori
    		self.gm_button_library.RegisterGroupWithColors(
    			"main_gm",
    			{name: (getattr(self, name), colors_per_button[name]) for name in button_names}
    		)
    
    	# =======================================
    	# Resize / Pickable / GM Layout
    	# =======================================
    	def ResizeWhisperDialog(self):
    		if not self.resizeButton or not self.board or not self.gm_buttons_board:
    			return
    
    		xPos, yPos = self.resizeButton.GetLocalPosition()
    		if xPos < 280: self.resizeButton.SetPosition(280, yPos); return
    		if yPos < 150: self.resizeButton.SetPosition(xPos, 150); return
    
    		self.SetWhisperDialogSize(xPos + 20, yPos + 20)
    
    		board_width = self.GetWidth() - (self.gm_board_padding_left + self.gm_board_padding_right)
    		self.gm_buttons_board.SetSize(board_width, self.gm_buttons_board.GetHeight())
    		self.__UpdateGMBoardLayout()
    
    
    	def __ArrangeGMButtons(self):
    		gm_buttons = self.gm_test_buttons  # lista cu toate butoanele GM
    
    		# =======================================
    		# Padding-ul inițial al board-ului
    		# =======================================
    		padding_left = self.gm_button_padding_left	# distanță minimă stânga față de board
    		padding_right = self.gm_button_padding_right# distanță minimă dreapta față de board
    
    		padding_top = self.gm_board_padding_top      # distanță sus față de bordura board
    		spacing_x = self.gm_button_spacing_horizontal  # spațiu între butoanele de pe același rând
    		spacing_y = self.gm_button_spacing_vertical    # spațiu între rânduri
    
    		board_width = self.gm_buttons_board.GetWidth()          # lățimea curentă a board-ului
    		usable_width = board_width - padding_left - padding_right  # spațiul efectiv pentru butoane
    
    		rows = []           # lista de rânduri
    		current_row = []    # butoanele din rândul curent
    		current_row_width = 0  # lățimea acumulată a rândului curent
    
    		# =======================================
    		# Creare rânduri
    		# =======================================
    		for btn in gm_buttons:
    			btn_width = btn.GetWidth()
    			# dacă adaug butonul curent + spacing depășește usable_width => fac rând nou
    			next_width = current_row_width + (spacing_x if current_row else 0) + btn_width
    			if next_width <= usable_width:
    				current_row.append(btn)
    				current_row_width = next_width
    			else:
    				rows.append(current_row)   # finalizez rândul curent
    				current_row = [btn]        # încep rând nou
    				current_row_width = btn_width
    
    		if current_row:
    			rows.append(current_row)       # adaug ultimul rând
    
    		# =======================================
    		# Poziționare butoane pe board
    		# =======================================
    		y = padding_top
    		for row in rows:
    			row_width = sum(btn.GetWidth() for btn in row) + spacing_x * (len(row) - 1)
    
    			# calculăm spațiul extra pentru centrare
    			extra_space = max(0, usable_width - row_width)
    			start_x = padding_left + extra_space / 2  # centrare în spațiul extra fără a tăia padding
    
    			x = start_x
    			row_max_height = 0  # înălțimea maximă a butoanelor din rând
    
    			for btn in row:
    				# aici păstrăm padding-ul minim stânga/dreapta la resize
    				btn.SetPosition(int(x), int(y))   # poziționează butonul
    				x += btn.GetWidth() + spacing_x   # trece la următorul buton
    				row_max_height = max(row_max_height, btn.GetHeight())  # actualizează înălțimea rândului
    
    			y += row_max_height + spacing_y  # trece la rândul următor
    
    		# =======================================
    		# Setăm înălțimea finală a board-ului GM
    		# =======================================
    		final_height = max(y - spacing_y + self.gm_board_padding_bottom, self.gm_buttons_board_min_height)
    		self.gm_buttons_board.SetSize(board_width, final_height)
    
    
    
    	# =======================================
    	# EXEMPLE MODIFICARE PADDING / SPACING
    	# =======================================
    	# 1. Mărire padding stânga/dreapta board:
    	#    self.gm_board_padding_left = 10
    	#    self.gm_board_padding_right = 10
    	# 2. Mărire spațiu între butoane pe rând:
    	#    self.gm_button_spacing_horizontal = 8
    	# 3. Mărire spațiu între rânduri:
    	#    self.gm_button_spacing_vertical = 8
    	# 4. Distanța board față de whisper:
    	#    self.gm_buttons_distance_from_whisper = 5
    	# 5. Dimensiune minimă board:
    	#    self.gm_buttons_board_min_height = 30
    	# Toate modificările se aplică **pixel cu pixel** la poziționarea și centrare butoanelor.
    
    
    	def __UpdatePickableArea(self):
    		if not chr.IsGameMaster(player.GetMainCharacterIndex()) or self.targetName in (0, "", None):
    			self.SetPickableAreaHeight(self.GetHeight())  # Dacă nu e GM sau target gol, folosește înălțimea normală
    			return
    		total_height = self.GetHeight() + self.gm_buttons_distance_from_whisper + self.gm_buttons_board.GetHeight()  # Include board GM
    		self.SetPickableAreaHeight(total_height)  # Aplică înălțimea totală
    
    	def SetPickableAreaHeight(self, newHeight):
    		self.pickable_height = newHeight  # Salvează înălțimea pickable
    		self.SetSize(self.GetWidth(), newHeight)  # Aplică dimensiunea ferestrei
    
    	def __UpdateGMBoardLayout(self):
    		self.__ArrangeGMButtons()
    		self.__UpdateGMBoardPosition()
    		self.__UpdatePickableArea()
    
    
    	def __UpdateGMBoardPosition(self):
    		board_x = self.gm_board_padding_left
    		board_y = self.GetHeight() + self.gm_buttons_distance_from_whisper
    		self.gm_buttons_board.SetPosition(board_x, board_y)
    
    	# =======================================
    	# SETAREA DIMENSIUNII WHISPER
    	# =======================================
    	def SetWhisperDialogSize(self, width, height):
    		try:
    
    			max = int((width-90)/6) * 3 - 6  # Calculează numărul maxim de linii chat bazat pe lățime
    
    			self.board.SetSize(width, height)  # Setează dimensiunea board-ului principal whisper
    			self.scrollBar.SetPosition(width-25, 35)  # Setează poziția scroll bar-ului
    			self.scrollBar.SetScrollBarSize(height-100)  # Setează înălțimea scroll bar-ului
    			self.scrollBar.SetPos(1.0)  # Inițializează scroll bar-ul la poziția maximă
    			self.editBar.SetSize(width-18, 50)  # Setează dimensiunea barei de editare
    			self.chatLine.SetSize(width-90, 40)  # Setează dimensiunea liniei de chat
    			self.chatLine.SetLimitWidth(width-90)  # Limitează lățimea liniei de chat
    			self.SetSize(width, height)  # Setează dimensiunea generală a ferestrei Whisper
    
    			if 0 != self.targetName:
    				chat.SetWhisperBoxSize(self.targetName, width - 50, height - 90)  # Update chat box pentru target
    
    			if localeInfo.IsARABIC():  # Verifică dacă limba este RTL (arabic)
    				self.textRenderer.SetPosition(width-20, 28)  # Mută text renderer pentru RTL
    				self.scrollBar.SetPosition(width-25+self.scrollBar.GetWidth(), 35)  # Ajustează scroll bar
    				self.editBar.SetPosition(10 + self.editBar.GetWidth(), height-60)  # Ajustează bara de editare
    				self.sendButton.SetPosition(width - 80 + self.sendButton.GetWidth(), 10)  # Ajustează buton send
    				self.minimizeButton.SetPosition(width-42 + self.minimizeButton.GetWidth(), 12)  # Ajustează minimize
    				self.closeButton.SetPosition(width-24+self.closeButton.GetWidth(), 12)  # Ajustează close
    				self.chatLine.SetPosition(5 + self.chatLine.GetWidth(), 5)  # Ajustează linia chat
    				self.board.SetPosition(self.board.GetWidth(), 0)  # Ajustează board
    			else:
    				self.textRenderer.SetPosition(20, 28)  # Poziția normală text renderer
    				self.scrollBar.SetPosition(width-25, 35)  # Poziția normală scroll bar
    				self.editBar.SetPosition(10, height-60)  # Poziția normală edit bar
    				self.sendButton.SetPosition(width-80, 10)  # Poziția normală buton send
    				self.minimizeButton.SetPosition(width-42, 12)  # Poziția normală minimize
    				self.closeButton.SetPosition(width-24, 12)  # Poziția normală close
    
    			self.SetChatLineMax(max)  # Setează linii max chat conform calculului
    
    		except:
    			import exception  # Import modul de gestiune erori
    			exception.Abort("WhisperDialog.SetWhisperDialogSize.BindObject")  # Afișează eroare dacă ceva nu merge
    
    
    	# =======================================
    	# UPDATE MAX CHAT LINES
    	# =======================================
    	def SetChatLineMax(self, max):
    		self.chatLine.SetMax(max)  # Setează numărul maxim de caractere / linie
    
    		from grpText import GetSplitingTextLine  # Import funcție pentru împărțirea textului pe linii
    
    		text = self.chatLine.GetText()  # Ia textul curent din chat
    		if text:
    			self.chatLine.SetText(GetSplitingTextLine(text, max, 0))  # Reformatează textul dacă e prea lung
    
    
    	# =======================================
    	# DESCHIDERE WHISPER CU TARGET
    	# =======================================
    	def OpenWithTarget(self, targetName):
    		chat.CreateWhisper(targetName)  # Crează whisper pe server pentru target
    		chat.SetWhisperBoxSize(targetName, self.GetWidth() - 60, self.GetHeight() - 90)  # Setează dimensiune chat box
    		self.chatLine.SetFocus()  # Focus pe linia de chat
    		self.titleName.SetText(targetName)  # Setează titlul ferestrei
    		self.targetName = targetName  # Salvăm intern numele targetului
    		self.textRenderer.SetTargetName(targetName)  # TextRenderer știe ce să afișeze
    		self.titleNameEdit.Hide()  # Ascunde inputul pentru nume
    		self.minimizeButton.Show()  # Arată butonul minimize
    		self.acceptButton.Hide()  # Ascunde butonul accept
    		self.ignoreButton.Hide()
    		if app.IsDevStage():
    			self.reportViolentWhisperButton.Show()
    		else:
    			self.reportViolentWhisperButton.Hide()
    
    
    		# =======================================
    		# GESTIONARE BUTOANE GM
    		# =======================================
    		if chr.IsGameMaster(player.GetMainCharacterIndex()):  # Dacă suntem GM
    			self.gm_button_library.ShowGroup("main_gm")  # Arată grupul de butoane GM
    			self.gm_buttons_board.Show()  # Arată board-ul GM
    		else:
    			self.gm_buttons_board.Hide()  # Ascunde board-ul GM
    			self.gm_button_library.HideGroup("main_gm")  # Ascunde butoanele GM
    
    		self.ResizeWhisperDialog()  # Ajustează dimensiunea și poziția elementelor după deschidere
    
    
    	# =======================================
    	# DESCHIDERE WHISPER FARA TARGET
    	# =======================================
    	def OpenWithoutTarget(self, event):
    		self.eventAcceptTarget = event  # Salvăm callback pentru acceptarea targetului
    		self.titleName.SetText("")  # Reset titlu
    		self.titleNameEdit.SetText("")  # Golim inputul
    		self.titleNameEdit.SetFocus()  # Focus pe input
    		self.targetName = 0  # Nu există target
    
    		self.titleNameEdit.Show()  # Arată inputul
    		self.gamemasterMark.Hide()  # Ascunde mark-ul GM
    		self.acceptButton.Show()  # Arată buton accept
    		self.minimizeButton.Show()  # Arată buton minimize
    		self.ignoreButton.Hide()
    		self.reportViolentWhisperButton.Hide()
    
    
    		# =======================================
    		# ASCUNDERE BUTOANE GM
    		# =======================================
    		self.gm_button_library.HideGroup("main_gm")  # Ascunde toate butoanele GM
    		self.gm_buttons_board.Hide()  # Ascunde board-ul GM
    
    		# =======================================
    		# UPDATE DIMENSIUNE + PICKABLE
    		# =======================================
    		self.ResizeWhisperDialog()  # Ajustează dimensiunea dialogului
    		self.SetPickableAreaHeight(self.GetHeight())  # Pickable doar whisper
    
    
    	# =======================================
    	# SET GM LOOK
    	# =======================================
    	def SetGameMasterLook(self):
    		self.gamemasterMark.Show()  # Arată iconița GM
    
    
    	# =======================================
    	# MINIMIZE
    	# =======================================
    	def Minimize(self):
    		self.titleNameEdit.KillFocus()  # Scoate focusul de pe input
    		self.chatLine.KillFocus()  # Scoate focusul de pe chatLine
    		self.Hide()  # Ascunde fereastra
    
    		if None != self.eventMinimize:
    			self.eventMinimize(self.targetName)  # Rulează callback pentru minimize
    
    
    	# =======================================
    	# CLOSE
    	# =======================================
    	def Close(self):
    		chat.ClearWhisper(self.targetName)  # Curăță whisper box
    		self.titleNameEdit.KillFocus()  # Scoate focusul de pe input
    		self.chatLine.KillFocus()  # Scoate focusul de pe chatLine
    		self.Hide()  # Ascunde fereastra
    
    		if None != self.eventClose:
    			self.eventClose(self.targetName)  # Rulează callback pentru close
    
    
    	# =======================================
    	# ACCEPT TARGET
    	# =======================================
    	def AcceptTarget(self):
    		name = self.titleNameEdit.GetText()  # Ia textul introdus
    		if len(name) <= 0:  # Dacă nu s-a introdus nimic
    			self.Close()  # Închide fereastra
    			return
    
    		if None != self.eventAcceptTarget:  # Dacă există callback
    			self.titleNameEdit.KillFocus()  # Scoate focusul
    			self.eventAcceptTarget(name)  # Rulează callback cu numele targetului
    
    
    	# =======================================
    	# SCROLL WHISPER
    	# =======================================
    	def OnScroll(self):
    		chat.SetWhisperPosition(self.targetName, self.scrollBar.GetPos())  # Actualizează poziția scrollului în chat
    
    
    	# =======================================
    	# TRIMITERE WHISPER
    	# =======================================
    	def SendWhisper(self):
    		text = self.chatLine.GetText()
    		if len(text) <= 0:
    			return
    
    		if net.IsInsultIn(text):
    			chat.AppendChat(chat.CHAT_TYPE_INFO, localeInfo.CHAT_INSULT_STRING)
    			return
    
    		emoji_map = {
    			":)": "|Eemoji/e_happy|e",
    			":(": "|Eemoji/e_sad|e",
    			":O": "|Eemoji/e_surprised|e",
    			":bored:": "|Eemoji/e_bored|e",
    			":poop:": "|Eemoji/e_cacat|e",
    			"B-)": "|Eemoji/e_cocalar|e",
    			".l.": "|Eemoji/e_cadoufa|e",
    			":virus:": "|Eemoji/e_coronafrt|e",
    			":D": "|Eemoji/e_d|e",
    			":P": "|Eemoji/e_p|e",
    			":hot:": "|Eemoji/e_estihotvtm|e",
    			";)": "|Eemoji/e_faccuochiufrt|e",
    			":facepalm:": "|Eemoji/e_facepalm|e",
    			":fire:": "|Eemoji/e_fire|e",
    			":muie:": "|Eemoji/e_fuckyou|e",
    			":cenzurat:": "|Eemoji/e_fututirasamatii|e",
    			":goodbye:": "|Eemoji/e_haipa|e",
    			"<3": "|Eemoji/e_inima|e",
    			":inlove:": "|Eemoji/e_inlove|e",
    			":*": "|Eemoji/e_kissyou|e",
    			":,(": "|Eemoji/e_lakrima|e",
    			":thinking:": "|Eemoji/e_magandesclamata|e",
    			":monkey:": "|Eemoji/e_maimuta|e",
    			":speriat:": "|Eemoji/e_maisperiat|e",
    			"=))": "|Eemoji/e_mapispatn|e",
    			":serios:": "|Eemoji/nicimatanutesuporta|e",
    			"=((": "|Eemoji/e_pling|e",
    			":clown:": "|Eemoji/e_pozacuatr|e",
    			":rip:": "|Eemoji/e_rip|e",
    			":rusine:": "|Eemoji/e_rusine|e",
    			":nebunie:": "|Eemoji/e_santnebundupatnfa|e",
    			":shh:": "|Eemoji/e_shh|e",
    			":fantoma:": "|Eemoji/e_svfantomacaspai2|e",
    			":vomit:": "|Eemoji/e_vomitpatn|e",
    			":corona:": "|Eemoji/e_anticorona|e",
    			":banana:": "|Eemoji/e_banana|e"
    		}
    
    		for key, val in emoji_map.items():
    			if key in text:
    				text = text.replace(key, val)
    
    		net.SendWhisperPacket(self.targetName, text)
    		self.chatLine.SetText("")
    		chat.AppendWhisper(chat.WHISPER_TYPE_CHAT, self.targetName, player.GetName() + " : " + text)
    
    
    
    	# =======================================
    	# ON TOP FOCUS
    	# =======================================
    	def OnTop(self):
    		self.chatLine.SetFocus()  # Focus pe linia de chat
    
    
    	# =======================================
    	# BIND INTERFACE
    	# =======================================
    	def BindInterface(self, interface):
    		self.interface = interface  # Salvează referința către interface
    
    
    	# =======================================
    	# MOUSE CLICK PE LINK
    	# =======================================
    	def OnMouseLeftButtonDown(self):
    		hyperlink = ui.GetHyperlink()  # Ia hyperlink-ul sub cursor
    		if hyperlink:
    			if app.IsPressed(app.DIK_LALT):  # Dacă ALT este apăsat
    				link = chat.GetLinkFromHyperlink(hyperlink)  # Extrage link
    				ime.PasteString(link)  # Lipește în input
    			else:
    				self.interface.MakeHyperlinkTooltip(hyperlink)  # Arată tooltip pentru link
    
    
    	# =======================================
    	# TEST UI STANDALONE
    	# =======================================
    	if "__main__" == __name__:
    		import uiTest  # Import modul test UI
    
    		class TestApp(uiTest.App):
    			def OnInit(self):
    				wnd = WhisperDialog(self.OnMax, self.OnMin)  # Creează instanță WhisperDialog
    				wnd.LoadDialog()  # Încarcă elementele UI
    				wnd.OpenWithoutTarget(self.OnNew)  # Deschide fără target
    				wnd.SetPosition(0, 0)  # Poziție colț stânga sus
    				wnd.Show()  # Arată fereastra
    
    				self.wnd = wnd  # Salvează referința pentru acces ulterior
    
    			def OnMax(self):
    				pass  # Callback pentru maximize
    
    			def OnMin(self):
    				pass  # Callback pentru minimize
    
    			def OnNew(self):
    				pass  # Callback pentru creare nou target
    
    		TestApp().MainLoop()  # Pornește bucla principală UI
    
    
    

    *Poze:

    [Py] GM Board Buttons – sistem UI integrat în Whisper (layout dinamic + resize) - Mesaj 1 - Imagine 1
    [Py] GM Board Buttons – sistem UI integrat în Whisper (layout dinamic + resize) - Mesaj 1 - Imagine 2
    [Py] GM Board Buttons – sistem UI integrat în Whisper (layout dinamic + resize) - Mesaj 1 - Imagine 3

    *Link download: Descarca poze de referinta si uiwhisper.py cu noul sistem board gm

    Nou Cum descarc de pe TeraBox?

    Afișează detalii Ascunde detalii
    • Este asemănător cu Mega.nz
    • Instalați-vă clientul lor de Download de aici
    • Faceți-vă un cont (vă puteți loga cu Facebook / Google / etc)
    • Nou Dacă nu vreți să descărcați clientul de Download, folosiți acest site
    • Gata! Acum puteți descărca resursele rapid & simplu.

    De ce folosim TeraBox?

    • Este gratuit
    • Primești 1TB de spațiu gratuit la orice cont creat!
    • Este ușor de folosit și varianta premium este foarte ieftină
    • Fișierele nu sunt șterse niciodată
    TeraBox logo

    📢 Resurse Metin2 Premium!

    Zeci de resurse Metin2 Premium - exclusive și 100% funcționale începând cu 15.99€!.

    Vezi resursele Cumpără premium
    Premium
    Premium
    Anunț

    Creează-ți un cont sau autentifică-te pentru a participa la discuție

    Trebuie să fii membru pentru a răspunde

    Creează-ți un cont

    Membrii pot crea subiecte noi și pot descărca resurse Metin2 Gratuit!


    Te poți înregistra sau conecta rapid utilizând contul tău de Discord, Github sau Google.

    Înregistrare

    Autentifică-te

    Înapoi la “Sisteme Metin2”

    Informații

    Utilizatori ce navighează pe acest forum: Angelic, dany8368, Dintisor, Dreu Zmeu, laurentiu_hetruc și 2 vizitatori

    Discord ID copiat: