PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » Вопросы по PureBasic » ScintillaGadget - нативное переключение документов.


ScintillaGadget - нативное переключение документов.

Сообщений 1 страница 8 из 8

1

Ребят, кто-то может изучал тему переключения гаджета Scintilla по вкладкам. Т.е. переключаешься по вкладкам как в IDE или в Notepad++ и включается новый документ, связанный с этой вкладкой. Соответственно, история сохраняется для каждого документа. Создавать для каждой вкладки новый гаджет, дело не хитрое, но это костыль, в особенности, если эта возможность, уже заложена нативно.
Внутри компонента Scintilla, есть внутренний движок для этого. Он описан в документации (если я правильно понял): Doc
Что-то я нащупал, работает несколько раз, потом вылетает по памяти. Там есть ссылки документов, но логики пока не понял. Может кто-то сможет доходчиво объяснить этот раздел документации. Мне бы конечно реально рабочий пример.

Отредактировано Webarion (22.02.2023 18:45:43)

0

2

На сколько я понимаю Scintilla создаёт один объект и не создаёт их нативно сколько угодно. Есть один объект и один дескриптор. Допускается 2 нативно в одном окне (в Notepad++ но это два объекта с разными вкладками), да это поддерживается (может больше), но редактор любой создаёт столько Scintilla, сколько вкладок в редакторе. Каждый Scintilla содержит документ собственный и при работе будет получен его дескритор и любая команда с этим дескриптором работает с конкретным документом- файлом. То есть в итоге надо создавать число Scintilla сколько юзер захочет открыть файлов. В момент открытия какой либо документ становится активным и с ним производится работа. Надо каждый раз проверять какой документ активный или кешировать хендл при смене вкладки. Для примера возьми любой редактор меняй вкладки и получай дескриптор активного документа с помощью Au3Info.exe и будет доказательство что у каждого свой дескриптор, а значит это свой собственный документ и выделенная для него память. А в dll находятся всего лишь функции по работе с документом.

Судя по описанию того раздела, там явно дано понять что это множественный просмотр, то есть не вкладки, а два документа в одном окне. То есть это не тоже самое что создавать 15 документов используя один объект.
То есть нативно допускается просмотр 2-х и более документов в одном окне, но это не значит что это один и тот же дескриптор, один и тот же объект-документ.

Отредактировано AZJIO (22.02.2023 19:20:50)

0

3

AZJIO написал(а):

Судя по описанию того раздела, там явно дано понять что это множественный просмотр, то есть не вкладки, а два документа в одном окне. То есть это не тоже самое что создавать 15 документов используя один объект.
То есть нативно допускается просмотр 2-х и более документов в одном окне, но это не значит что это один и тот же дескриптор, один и тот же объект-документ.

Я тоже думал об этом, но вроде получилось. Не знаю на сколько это правильно и не будет ли утечки памяти. Но пока, этот код работает. Если нажимать на кнопки, то будут переключаться документы в одном ScintillaGadget:

Код:
#Win = 0
#Sci = 0

; возвращает рандомный текст
Procedure.s _Get_Random_Text()
  Protected Text$ = ""
  For i=0 To 300
    Define Rnd = Random(90, 65)
    Text$ + Chr( Rnd )
    If Rnd < 67: Text$ + #CRLF$ : EndIf
  Next
  ProcedureReturn Text$
EndProcedure

; событие клика по кнопке
Procedure _Event_Click_Tab()
  Protected Button  = EventGadget() ; получаем кнопку
  Protected *SciDoc = GetGadgetData(Button) ; получаем записанный указатель на документ
  ScintillaSendMessage( #Sci, #SCI_SETDOCPOINTER, 0, *SciDoc ) ; устанавливаемся на документ
EndProcedure


If OpenWindow( #Win, 0, 0, 700, 500, "Пример", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget )
  
  If Not InitScintilla() : MessageRequester("", "Не удалось инициализировать компонент Scintilla" ) : End : EndIf
  ; создаём один Scintilla гаджет
  ScintillaGadget( #Sci, 3, 25, WindowWidth(#Win)-7, WindowHeight(#Win)-27, 0 ) 
  ScintillaSendMessage( #Sci, #SCI_STYLESETFORE, 0, $006400 ) ; покрасим основной текст
  
  Define CountTabs = 8 ; сколько будет табов
  
  For i=0 To CountTabs-1
    Define Button = #Sci+i+1 ; это будут кнопочки после ScintillaGadget
    ButtonGadget(Button, 3+i*50, 3, 50, 20, "Tab"+Str(i+1) )
    
    ; Пишем текст в текущий выбранный документ
    Define Text$ = _Get_Random_Text()
    Define *Text=UTF8(Text$)
    ScintillaSendMessage(#Sci, #SCI_APPENDTEXT, Len(Text$), *Text)
    FreeMemory(*Text)      
    
    Define *SciDoc = ScintillaSendMessage(#Sci, #SCI_GETDOCPOINTER ) ; получаем дескриптор текущего документа
    ScintillaSendMessage(#Sci, #SCI_ADDREFDOCUMENT, 0, *SciDoc )     ; создаём следующий документ
    ScintillaSendMessage(#Sci, #SCI_SETDOCPOINTER, 0, 0 )            ; устанавливаемся на новый документ
    SetGadgetData(Button, *SciDoc)                                   ; привязываем документ к кнопке
    
    
    BindGadgetEvent(Button, @_Event_Click_Tab(), 0 ) 
    
  Next
  
  ; показываем документ, связанный с последней кнопкой
  ScintillaSendMessage( #Sci, #SCI_SETDOCPOINTER, 0, GetGadgetData(Button) )
  
  Repeat
    Define Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
  
EndIf

Отредактировано Webarion (22.02.2023 22:05:13)

0

4

Webarion написал(а):

Создавать для каждой вкладки новый гаджет, дело не хитрое, но это костыль

Почему? Это нормальная практика.

0

5

Пётр написал(а):

Почему? Это нормальная практика.

Каждому своё, как говорится. Я пробовал, мне не понравилось. При переключении, гаджеты мерцают. Для каждого нужно задавать все параметры, подсветку. В моё понимание нормальности, это не укладывается. Scintilla увесистый гаджет. Иметь кучу таких объектов, при многих открытых вкладках, при этом зная, что внутри самого компонента уже заложена такая возможность. Я бы себе не простил. Лучше чуть исследую и постараюсь сделать красиво. Тем более что результат есть.
Посмотрев исходники Scintilla, я увидел, что там действительно есть система документов и ссылок. Поэтому как и AZJIO прав, сказав о множественном просмотре, так и я прав, сказав о переключении документов. Этот раздел в документации относится и к тому и к другому.

Отредактировано Webarion (24.02.2023 03:09:46)

0

6

Сделал используя PanelGadget, это лучше чем кнопки, так как если не умещается то есть прокрутка.
https://www.purebasic.fr/english/viewtopic.php?t=81501

Ещё бы сделать прокрутку колесом, вроде не должно быть сложностей, при событии вращения колёсика получить индекс добавить +1 сделать вкладку активной.

0

7

AZJIO написал(а):

https://www.purebasic.fr/english/viewtopic.php?t=81501

Если выделить текст, то при переключении документов выделение теряется. Тоже происходит с позицией прокрутки. Думаю с подсветкой будет аналогично.
Все же это не замена экземплярам редактора.

0

8

Пётр написал(а):

Если выделить текст, то при переключении документов выделение теряется. Тоже происходит с позицией прокрутки. Думаю с подсветкой будет аналогично.
Все же это не замена экземплярам редактора.

С подсветкой не аналогично. При переключении, она не теряется, т.е. привязана к копии одного документа. Некоторый другой функционал, действительно сбрасывается. Но это не сложно проконтролировать. В итоге, мы используем специально предназначенный для этого инструментарий имеющийся в Scintilla. Конечно, куда было бы проще накидать гаджетов и привязать их к вкладкам. Но, как я уже отметил, мне это не по нраву. Поэтому делаю то, что нравится. Пример с сохранением подсветки, выделения, прокрутки, положения каретки и блоков сворачивания:

Код:
; Описание ...: Для каждой вкладки PanelGadget создаётся внутренний документ одного ScintillaGadget
; Автор ......: Webarion
; Примечание .: Это тестовый код, здесь есть недоработки.

DeclareModule SciDocTab
  
  Enumeration Commands
    #Tab_Add
    #Tab_Delete
    #Tab_Index
    #Tab_Count
    #Tab_Select
  EndEnumeration
  
  Prototype UserProc_Handler( Command, TabGadget, TabIndex = -1 )
  
  Structure Doc_SDT
    *SciDoc           ; указатель на документ Scintilla, связанный с вкладкой
    Anchor.i          ; позиция якоря
    CaretPos.i        ; положение каретки  
    FirstLine.i       ; положение верхней строки
    List FoldLine.i() ; список свёрнутых строк
  EndStructure
  
  Structure TabGadget
    SciGadget.i
    UserProc_Handler.UserProc_Handler
    List Doc.Doc_SDT()
  EndStructure
  
  Global NewMap TabGadget.TabGadget()
  
  Declare Register( ScintillaGadget, TabGadget, *CallbackProcedure )
  Declare AddTab( TabGadget )
  Declare SelectTab( TabGadget, TabIndex = -1 )
  Declare DeleteTab( TabGadget, TabIndex = -1 )
  
EndDeclareModule

Module SciDocTab
  
  Procedure _UserProc_Controller( Command, TabGadget, TabIndex = -1 )
    If FindMapElement( TabGadget(), Str(TabGadget) )
      With TabGadget()
        If \UserProc_Handler 
          ProcedureReturn \UserProc_Handler( Command, TabGadget, TabIndex )
        EndIf
      EndWith
    EndIf
  EndProcedure
  
  Procedure Register( ScintillaGadget, TabGadget, *UserProc_Handler )
    With TabGadget( Str(TabGadget) )
      \SciGadget = ScintillaGadget
      \UserProc_Handler = *UserProc_Handler
    EndWith
  EndProcedure
  
  Procedure AddTab( TabGadget )
    
    With TabGadget()
      
      Protected This_Tab = _UserProc_Controller( #Tab_Index, TabGadget )
      
      If This_Tab > -1
        SelectElement( \Doc(), This_Tab )
      EndIf
      
      LastElement( \Doc() )
      
      AddElement( \Doc() )
      NewIndex = ListIndex( \Doc() )
      
      _UserProc_Controller( #Tab_Add, TabGadget, NewIndex )
      
      Protected DocsCount = ListSize( \Doc() )
      
      If DocsCount = 1
        \Doc()\SciDoc = ScintillaSendMessage(\SciGadget, #SCI_GETDOCPOINTER)
        ScintillaSendMessage(\SciGadget, #SCI_ADDREFDOCUMENT, 0, \Doc()\SciDoc)
      ElseIf DocsCount > 1
        \Doc()\SciDoc = ScintillaSendMessage(\SciGadget, #SCI_CREATEDOCUMENT)
      EndIf
      ;       ScintillaSendMessage( \SciGadget, #SCI_SETDOCPOINTER, 0, \Doc()\SciDoc )
    EndWith
    
    SelectTab( TabGadget, NewIndex )
    
  EndProcedure
  
  Procedure SelectTab( TabGadget, TabIndex = -1 )
    
    With TabGadget()
      
      Static Old_Select_TabIndex = -1
      If Old_Select_TabIndex <> -1
        
        Protected *OldDoc_SDT.Doc_SDT = SelectElement( \Doc(), Old_Select_TabIndex)
        
        If *OldDoc_SDT
          *OldDoc_SDT\Anchor    = ScintillaSendMessage(\SciGadget, #SCI_GETANCHOR)
          *OldDoc_SDT\CaretPos  = ScintillaSendMessage(\SciGadget, #SCI_GETCURRENTPOS)
          *OldDoc_SDT\FirstLine = ScintillaSendMessage(\SciGadget, #SCI_GETFIRSTVISIBLELINE)
          Protected lineStart = 0
          Repeat 
            Protected FoldLine = ScintillaSendMessage(\SciGadget, #SCI_CONTRACTEDFOLDNEXT, lineStart) 
            If FoldLine <> -1
              AddElement( *OldDoc_SDT\FoldLine() ) : *OldDoc_SDT\FoldLine() = FoldLine
            EndIf
            lineStart = FoldLine + 1
          Until FoldLine = -1
        EndIf
      EndIf
      
      If TabIndex = -1
        TabIndex = _UserProc_Controller( #Tab_Index, TabGadget )
      EndIf
      
      If TabIndex > ListSize( \Doc() ) - 1
        ProcedureReturn
      EndIf
      
      If TabIndex <> -1
        Protected *Doc_SDT.Doc_SDT = SelectElement( \Doc(), TabIndex )
        ScintillaSendMessage(\SciGadget, #SCI_SETDOCPOINTER, 0, *Doc_SDT\SciDoc)
        ScintillaSendMessage(\SciGadget, #SCI_SETFIRSTVISIBLELINE, *Doc_SDT\FirstLine)
        ScintillaSendMessage(\SciGadget, #SCI_SETSEL, *Doc_SDT\Anchor, *Doc_SDT\CaretPos)
        ForEach *Doc_SDT\FoldLine()
          ScintillaSendMessage(\SciGadget, #SCI_FOLDLINE, *Doc_SDT\FoldLine(), 0 ) 
        Next
        
        SetActiveGadget(\SciGadget)
        
        _UserProc_Controller( #Tab_Select, TabGadget, TabIndex )
        
        Old_Select_TabIndex = TabIndex
        
      EndIf
      
    EndWith
    
  EndProcedure
  
  Procedure DeleteTab( TabGadget, TabIndex = -1 )
    
    With TabGadget()
      
      If TabIndex = -1
        TabIndex  = _UserProc_Controller( #Tab_Index, TabGadget )
      EndIf 
      
      Protected CountTabs = _UserProc_Controller( #Tab_Count, TabGadget )
      
      If TabIndex > -1 And CountTabs > 1
        Protected *Doc.Doc_SDT = SelectElement( \Doc(), TabIndex)
        ScintillaSendMessage(\SciGadget, #SCI_RELEASEDOCUMENT, 0, *Doc\SciDoc)
        
        DeleteElement( \Doc() )
        
        If CountTabs > 1 
          
          _UserProc_Controller( #Tab_Delete, TabGadget, TabIndex )
          
          CountTabs = ListSize( \Doc() )
          If TabIndex = CountTabs
            _UserProc_Controller( #Tab_Select, TabGadget, CountTabs-1 )
          Else
            _UserProc_Controller( #Tab_Select, TabGadget, TabIndex )
          EndIf
          
          SelectTab(TabGadget)
          
        EndIf
      EndIf
      
    EndWith
    
  EndProcedure
  
EndModule


; ПРИМЕР для PanelGadget

CompilerIf #PB_Compiler_IsMainFile 
  
  If Not InitScintilla() : MessageRequester("", "Не удалось инициализировать компонент Scintilla" ) : End : EndIf
  
  Enumeration Gadgets
    #Sci
    #Sci2
    #ButAdd
    #ButAdd2
    #ButDel
    #ButDel2
    #Panel
    #Panel2
    #TabBar
  EndEnumeration
  
  ; Обработчик вкладок PanelGadget
  Procedure _PanelGadget_TabCallback( Command, PanelGadget, TabIndex = -1 )
    Select Command
      Case SciDocTab::#Tab_Add
        ; создадим нумерацию для двух PanelGadget
        Static NumName1 = 1, NumName2 = 1
        Protected NumName
        If PanelGadget = #Panel 
          NumName = NumName1 : NumName1 + 1
        Else 
          NumName = NumName2 : NumName2 + 1
        EndIf
        OpenGadgetList(PanelGadget)
        AddGadgetItem(PanelGadget, TabIndex, "Вкладка " + Str(NumName) ) ; создаём новую вкладку
        CloseGadgetList()
        
      Case SciDocTab::#Tab_Delete
        RemoveGadgetItem(PanelGadget, TabIndex)
        
      Case SciDocTab::#Tab_Index
        ProcedureReturn GetGadgetState(PanelGadget)
        
      Case SciDocTab::#Tab_Count
        ProcedureReturn CountGadgetItems(PanelGadget)
        
      Case SciDocTab::#Tab_Select
        SetGadgetState( PanelGadget, TabIndex ) 
        
    EndSelect
  EndProcedure
  
  ; генератор текста и стиля для документа
  Procedure.s SciGenText(Sci)
    Protected Rnd, String.s = "" , Num
    RandomSeed(Random(100000, 1))
    While Num<50
      Rnd =Random(122, 48)
      Select Rnd
        Case 48 To 57, 65 To 90
          String + Chr(Rnd) : Num + 1
      EndSelect
    Wend
    Protected Pos
    Static Pos1 = 3, Pos2 = 3, N1 = 1, N2 = 1
    If Sci = #Sci
      Pos = Pos1 : Pos1 + 5 : String = Str(N1) + ") " + String : N1 + 1
    Else
      Pos = Pos2 : Pos2 + 5 : String = Str(N2) + ") " + String : N2 + 1
    EndIf  
    Define *Buff = UTF8(String)
    ScintillaSendMessage(Sci, #SCI_SETTEXT, 0, *Buff)
    FreeMemory(*Buff)
    ScintillaSendMessage(Sci, #SCI_STYLESETBACK, 1, $32CD9A) 
    ScintillaSendMessage(Sci, #SCI_STYLESETFORE, 1, $FF0000) 
    ScintillaSendMessage(Sci, #SCI_STARTSTYLING, Pos )
    ScintillaSendMessage(Sci, #SCI_SETSTYLING, 5, 1 )  
  EndProcedure
  
  If OpenWindow( 0, 0, 0, 500, 380, "Example PanelGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered )
    
    ButtonGadget(#ButAdd,  450, 10,  20, 20, "+") : ButtonGadget(#ButDel,  470, 10,  20, 20, "-")
    ButtonGadget(#ButAdd2, 450, 200, 20, 20, "+") : ButtonGadget(#ButDel2, 470, 200, 20, 20, "-")
    If LoadFont(0, "Arial", 16)
      Define FontID = FontID(0)
      SetGadgetFont(#ButAdd, FontID) : SetGadgetFont(#ButDel, FontID)
      SetGadgetFont(#ButAdd2, FontID) : SetGadgetFont(#ButDel2, FontID)
    EndIf
    
    PanelGadget(#Panel, 10, 10, 440, 22) : CloseGadgetList()
    PanelGadget(#Panel2, 10, 200, 440, 22) : CloseGadgetList()
    
    ScintillaGadget(#Sci, 10, 32, 480, 150, 0 )
    ScintillaGadget(#Sci2, 10, 222, 480, 150, 0 )
    
    ; покрасим основной цвет #Sci и #Sci2
    ScintillaSendMessage( #Sci, #SCI_STYLESETFORE, 0, $0045FF )
    ScintillaSendMessage( #Sci2, #SCI_STYLESETFORE, 0, $8515C7 )
    
    ; привязка и регистрация в SciDocTab
    SciDocTab::Register( #Sci, #Panel, @_PanelGadget_TabCallback() )
    SciDocTab::Register( #Sci2, #Panel2, @_PanelGadget_TabCallback() ) 
    
    ; Первая вкладка
    SciDocTab::AddTab( #Panel ) : SciGenText(#Sci)
    SciDocTab::AddTab( #Panel2 ) : SciGenText(#Sci2)
    
    Repeat
      Define eventID = WaitWindowEvent()
      Select eventID
        Case #PB_Event_Gadget
          Select EventGadget()
              ; for #Panel
            Case #ButAdd
              SciDocTab::AddTab(#Panel) : SciGenText(#Sci)
            Case #ButDel
              SciDocTab::DeleteTab(#Panel)
            Case #Panel
              SciDocTab::SelectTab(#Panel)
              ; for #Panel2
            Case #ButAdd2
              SciDocTab::AddTab(#Panel2) : SciGenText(#Sci2)
            Case #ButDel2
              SciDocTab::DeleteTab(#Panel2)
            Case #Panel2
              SciDocTab::SelectTab(#Panel2)
          EndSelect
      EndSelect
    Until eventID = #PB_Event_CloseWindow 
    
  EndIf
  
CompilerEndIf

Отредактировано Webarion (10.06.2023 17:28:56)

0


Вы здесь » PureBasic - форум » Вопросы по PureBasic » ScintillaGadget - нативное переключение документов.