DialogClass — модуль для работы с диалогами с стиле ООП

Здесь обсуждается всё, что касается макросов.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Smitis »

Модуль для работы с диалогами в стиле ООП.
  • Задание параметров диалога и элементов диалога в более нагладном виде - таблицами с именованными полями.
  • Вся работа с диалогом и элементами осуществляется вызовом соответствующих методов без непосредственного вызова far.SendDlgMessage и других функций из таблицы far.
  • Не требуется сохранять хендл диалога.
  • Нет необходимости считать вручную и где-то сохранять ID элементов. Элементы диалога имеют дополнительные поля, в том числе поле Name, позволяющее обращаться к элементам диалога по именам.
  • Специальные функции облегчают работу с созданием диалога "по строкам".
  • Дополнительные методы облегчат сохранение всех значений элементов в таблицу. И наоборот, заполнение значений элементов из таблицы.








Некоторые идеи честно стырены из исходников luafar4editor :)

DialogClass 1.15
Пример наследования классов: MultiPagesDialogClass
Last edited by Smitis on Mon 17 Jan, 2022 14:33, edited 1 time in total.
Разум когда-нибудь победит.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Smitis »

Latest post of the previous page:

Приводимый выше пример макроса ReName выявил некоторую несовместимость с новой версией модуля.
Вот совместимая
Если кто-то использует - макрос был написан для тестирования (хотя оказался полезен). Можно выкинуть варианты вызова, оставив только один, например
Разум когда-нибудь победит.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Smitis »

v.1.08
- Исправлена ошибка, связанная с использованием параметра W диалога.
- Метод AddMainFrame исключён.
- Метод Clear исключён так как оказался практически ненужным (проще заново конструктор вызвать).
Разум когда-нибудь победит.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Smitis »

v.1.09 Экспериментальная версия, поэтому в шапку пока не добавлял, вдруг что-нибудь всплывёт.
Продолжаю измываться над "классами" исполненными на Lua.
Для контейнера элементов диалога DialogItemsClass добавлена возможность... как бы это обозвать-то по-умному... "добавление новых именованных элементов диалога функциональным вызовом по их имени"... как-то так.
На практике это выгладит так, вместо присваивания DI.OK = { "b=!+", Text=lang.OkButton } можно писать DI.OK { "b=!+", Text=lang.OkButton }. Чисто для красивости. В примере RENAME добавление элементов этим способом будет выглядеть так:

Code: Select all

DI { "t+", Text=FileNamePrompt }
DI.FileName { "e+", History=cfg.FileNameHistory, Text=FileName }
DI { "t+", Text=FileExtPrompt }
DI { "t+", Text="." }
DI.FileExt { "e", X1=D.LC+1, History=cfg.FileExtHistory, Text=FileExt }
DI { "s+" }
DI.OK { "b=!+", Text=lang.OkButton }
DI.Cancel { "b=", Text=lang.CancelButton }
НЕОЖИДАННЫЕ побочные эффекты:
- Данный способ не позволяет добавлять элементы с одинаковыми именами. С одной строны плохо - остальные способы позволяют. С другой - хорошо, дополнительный контроль ошибок.
- В случае обращения по имени к несуществующему элементу вместо привычного сообщения, что элемент таблицы не существует, будет сообщение об ошибочном вызове функции.
  1. --[=[ ----------------------------------------------------------------------------------------------
  2.  
  3. Модуль для работы с диалогами в стиле ООП.
  4.  
  5. v. 1.09 (C) 2013-2015 Smitis
  6.  
  7. viewtopic.php?t=8523
  8.  
  9. --]=] ----------------------------------------------------------------------------------------------
  10.  
  11.  
  12.  
  13. local _version = "1.09"
  14.  
  15. local F = far.Flags
  16.  
  17.  
  18.  
  19. local function log2num(b) return tonumber(b) or (b and 1) or 0 end
  20.  
  21. local function state2num(b) return b==nil and -1 or log2num(b) end
  22.  
  23.  
  24.  
  25. -- Преобразование флагов, заданых в различном виде (число, строка, таблица) к единому числовому значению.
  26.  
  27. local function flags2num(fx)
  28.  
  29.     local fn = 0
  30.  
  31.     if type(fx)=="string" then -- строка вида "DI_BUTTON"
  32.  
  33.         fn = F[fx]
  34.  
  35.     elseif type(fx)=="table" then -- таблица вида {DIF_CENTERGROUP=1, DIF_DEFAULTBUTTON=1}
  36.  
  37.         for flag,v in pairs(fx) do
  38.  
  39.             if v~=0 then
  40.  
  41.                 fn = bor(fn,F[flag])
  42.  
  43.             end
  44.  
  45.         end
  46.  
  47.     else -- числовое значение или nil
  48.  
  49.         fn = fx or 0
  50.  
  51.     end
  52.  
  53.     return fn
  54.  
  55. end
  56.  
  57.  
  58.  
  59. ----------------------------------------------------------------------------------------------------
  60.  
  61. -- Класс DlgItem (элемент диалога)
  62.  
  63. ----------------------------------------------------------------------------------------------------
  64.  
  65.  
  66.  
  67. local DlgItemClass = {}
  68.  
  69.  
  70.  
  71. DlgItemClass.NamedFields = {
  72.  
  73.     Type=1, X1=2, Y1=3, X2=4, Y2=5, Selected=6, ListItems=6, VBuf=6,
  74.  
  75.     History=7, Mask=8, Separator=8, Flags=9, Data=10, Text=10, MaxLength=11, UserData=12
  76.  
  77. }
  78.  
  79.  
  80.  
  81. function DlgItemClass:__index(key)
  82.  
  83.     if DlgItemClass.NamedFields[key] then
  84.  
  85.         return rawget(self,DlgItemClass.NamedFields[key])
  86.  
  87.     end
  88.  
  89.     return rawget(self,key) or DlgItemClass[key]
  90.  
  91. end
  92.  
  93.  
  94.  
  95. function DlgItemClass:__newindex(key,val)
  96.  
  97.     rawset(self, DlgItemClass.NamedFields[key] or key, val)
  98.  
  99. end
  100.  
  101.  
  102.  
  103. -- Отправить сообщение диалогу для данного элемента
  104.  
  105. function DlgItemClass:Send(Msg,Param)
  106.  
  107.     return self.Parent:Send(Msg,self.Id,Param)
  108.  
  109. end
  110.  
  111. -- Вкл./выкл элемент диалога
  112.  
  113. function DlgItemClass:Enable(x)
  114.  
  115.     return self:Send(F.DM_ENABLE,state2num(x)) == 1
  116.  
  117. end
  118.  
  119. -- Установить состояние EDITUNCHANGEDFLAG
  120.  
  121. function DlgItemClass:Unchanged(x)
  122.  
  123.     return self:Send(F.DM_EDITUNCHANGEDFLAG,state2num(x)) == 1
  124.  
  125. end
  126.  
  127. -- Показать/скрыть элемент диалога
  128.  
  129. function DlgItemClass:Show(x)
  130.  
  131.     return self:Send(F.DM_SHOWITEM,state2num(x)) == 1
  132.  
  133. end
  134.  
  135. -- Установить фокус на элемент диалога
  136.  
  137. function DlgItemClass:Focus()
  138.  
  139.     return self:Send(F.DM_SETFOCUS,0) == 1
  140.  
  141. end
  142.  
  143. -- Установить значение CHECKBOX или RADIOBUTTON
  144.  
  145. function DlgItemClass:SetCheck(x)
  146.  
  147.     return self:Send(F.DM_SETCHECK,x)
  148.  
  149. end
  150.  
  151. -- Получить значение CHECKBOX или RADIOBUTTON
  152.  
  153. function DlgItemClass:GetCheck()
  154.  
  155.     return self:Send(F.DM_GETCHECK,0)
  156.  
  157. end
  158.  
  159. -- Установить логическое состояние CHECKBOX или RADIOBUTTON
  160.  
  161. function DlgItemClass:SetCheckState(x)
  162.  
  163.     return self:SetCheck(log2num(x)) >= 1
  164.  
  165. end
  166.  
  167. -- Получить логическое состояние CHECKBOX или RADIOBUTTON
  168.  
  169. function DlgItemClass:GetCheckState()
  170.  
  171.     return self:GetCheck() >= 1
  172.  
  173. end
  174.  
  175. -- Установить текстовое значение
  176.  
  177. function DlgItemClass:SetText(text)
  178.  
  179.     return self:Send(F.DM_SETTEXT,text)
  180.  
  181. end
  182.  
  183. -- Получить текстовое значение
  184.  
  185. function DlgItemClass:GetText()
  186.  
  187.     return self:Send(F.DM_GETTEXT,0)
  188.  
  189. end
  190.  
  191. -- Установить текущую позицию для списка
  192.  
  193. function DlgItemClass:SetListPos(pos,top)
  194.  
  195.     return self:Send(F.DM_LISTSETCURPOS,{SelectPos=pos,TopPos=top})
  196.  
  197. end
  198.  
  199. -- Получить текущую позицию для списка
  200.  
  201. function DlgItemClass:GetListPos()
  202.  
  203.     local FarListPos = self:Send(F.DM_LISTGETCURPOS,0)
  204.  
  205.     return FarListPos.SelectPos, FarListPos.TopPos
  206.  
  207. end
  208.  
  209. -- Установки значения в зависимости от типа элемента диалога
  210.  
  211. -- [???] Если val таблица, посылать DM_SETDLGITEM?
  212.  
  213. function DlgItemClass:SetValue(val,top)
  214.  
  215.     if self.Type==F.DI_CHECKBOX or self.Type==F.DI_RADIOBUTTON then
  216.  
  217.         return self:SetCheck(log2num(val))
  218.  
  219.     elseif self.Type==F.DI_LISTBOX then
  220.  
  221.         return self:SetListPos(val,top)
  222.  
  223.     elseif self.Type==F.DI_COMBOBOX then
  224.  
  225.         if type(val)=="number" then
  226.  
  227.             return self:SetListPos(val,top)
  228.  
  229.         else
  230.  
  231.             return self:SetText(tostring(val))
  232.  
  233.         end
  234.  
  235.     else
  236.  
  237.         return self:SetText(tostring(val))
  238.  
  239.     end
  240.  
  241. end
  242.  
  243. -- Получения значения в зависимости от типа элемента диалога
  244.  
  245. function DlgItemClass:GetValue()
  246.  
  247.     if self.Type==F.DI_CHECKBOX or self.Type==F.DI_RADIOBUTTON then
  248.  
  249.         return self:GetCheck()
  250.  
  251.     elseif self.Type==F.DI_LISTBOX then
  252.  
  253.         return self:GetListPos()
  254.  
  255.     else
  256.  
  257.         return self:GetText()
  258.  
  259.     end
  260.  
  261. end
  262.  
  263.  
  264.  
  265. ----------------------------------------------------------------------------------------------------
  266.  
  267. -- Класс DlgItems (контейнер для всех dlgitem)
  268.  
  269. -- Элементы с индексами <=0 являются private полями и нигде, кроме как здесь не должны использоваться.
  270.  
  271. -- local __named__  =  0 -- Таблица имён элементов
  272.  
  273. -- local __parent__ = -1 -- Ссылка на родительский диалог
  274.  
  275. -- local __framed__ = -2 -- Ссылка на элемент рамки, добавленной методом AddMainFrame. Если nil или false - рамка не добавлялась.
  276.  
  277. ----------------------------------------------------------------------------------------------------
  278.  
  279.  
  280.  
  281. local DlgItemsClass = {}
  282.  
  283. --[=[
  284.  
  285. function DlgItemsClass:__index(name)
  286.  
  287.     return rawget(self, self[0][name] or name)
  288.  
  289. end
  290.  
  291. --]=]
  292.  
  293. function DlgItemsClass:__index(name)
  294.  
  295.     --local item = rawget(self, self[0][name] or name)
  296.  
  297.     return rawget(self, self[0][name] or name) or function(newitem)
  298.  
  299.         newitem.Name = name
  300.  
  301.         self(newitem)
  302.  
  303.     end
  304.  
  305. end
  306.  
  307.  
  308.  
  309. -- Добавление нового элемента диалога
  310.  
  311. function DlgItemsClass:__newindex(name,item)
  312.  
  313.     local x
  314.  
  315.     if type(item)=="table" then
  316.  
  317.         local id = #self+1
  318.  
  319.         local parent = self[-1]
  320.  
  321.         item.Name = name or item.Name or "_"
  322.  
  323.         item.Id = id
  324.  
  325.         item.Parent = parent
  326.  
  327.         self[0][item.Name] = id -- в список именованных элементов
  328.  
  329.         -- копируем все виртуальные поля и удаляем их
  330.  
  331.         for f,v in pairs(item) do
  332.  
  333.             if DlgItemClass.NamedFields[f] then
  334.  
  335.                 item[DlgItemClass.NamedFields[f]] = v
  336.  
  337.                 item[f] = nil
  338.  
  339.             end
  340.  
  341.         end
  342.  
  343.  
  344.  
  345.         setmetatable(item,parent.DlgItemClass or DlgItemClass) -- объект
  346.  
  347.  
  348.  
  349.         item.Flags = flags2num(item.Flags) -- Flags [9] в number
  350.  
  351.         if type(item.Type)=="number" or item.Type:sub(1,3)=="DI_" then
  352.  
  353.             item.Type = item.Type and flags2num(item.Type) or F.DI_TEXT -- тип элемента в number или тип по умолчанию
  354.  
  355.         else -- Если тип - строка начинающаяся не с DI_, это сокращённое обозначение типа элемента, параметров, опций
  356.  
  357.             local typ = item.Type:sub(1,1)
  358.  
  359.             local opt = item.Type:sub(2)
  360.  
  361.             if typ == "b" then
  362.  
  363.                 item.Type = F.DI_BUTTON
  364.  
  365.                 if opt:find("b") then item.Flags = bor(item.Flags,F.DIF_NOBRACKETS) end
  366.  
  367.                 if opt:find(";") then item.Flags = bor(item.Flags,F.DIF_BTNNOCLOSE) end
  368.  
  369.             elseif typ == "f" then
  370.  
  371.                 item.Type = opt:find("d") and F.DI_DOUBLEBOX or F.DI_SINGLEBOX
  372.  
  373.             elseif typ == "t" then
  374.  
  375.                 item.Type = opt:find("v") and F.DI_VTEXT or F.DI_TEXT
  376.  
  377.                 -- DIF_WORDWRAP ???
  378.  
  379.             elseif typ == "e" then
  380.  
  381.                 item.Type = opt:find("f") and F.DI_FIXEDIT or F.DI_EDIT
  382.  
  383.                 if opt:find("o") then item.Flags = bor(item.Flags,F.DIF_READONLY) end
  384.  
  385.                 if opt:find("e") then item.Flags = bor(item.Flags,F.DIF_EDITOR) end
  386.  
  387.                 if opt:find("h") then item.Flags = bor(item.Flags,F.DIF_HISTORY) end
  388.  
  389.                 if opt:find("m") then item.Flags = bor(item.Flags,F.DIF_MANUALADDHISTORY) end
  390.  
  391.                 if opt:find("l") then item.Flags = bor(item.Flags,F.DIF_USELASTHISTORY) end
  392.  
  393.             elseif typ == "p" then
  394.  
  395.                 item.Type = F.DI_PSWEDIT
  396.  
  397.             elseif typ == "s" then -- DI_TEXT | DI_VTEXT & DIF_SEPARATOR | DIF_SEPARATOR2
  398.  
  399.                 item.Type = opt:find("v") and F.DI_VTEXT or F.DI_TEXT
  400.  
  401.                 item.Flags = bor(item.Flags,opt:find("d") and F.DIF_SEPARATOR2 or F.DIF_SEPARATOR)
  402.  
  403.             elseif typ == "r" then
  404.  
  405.                 item.Type = F.DI_RADIOBUTTON
  406.  
  407.             elseif typ == "c" then
  408.  
  409.                 item.Type = F.DI_CHECKBOX
  410.  
  411.                 if opt:find("3") then item.Flags = bor(item.Flags,F.DIF_3STATE) end
  412.  
  413.             elseif typ == "l" then
  414.  
  415.                 item.Type = F.DI_LISTBOX
  416.  
  417.             elseif typ == "x" then
  418.  
  419.                 item.Type = F.DI_COMBOBOX
  420.  
  421.                 if opt:find("d") then item.Flags = bor(item.Flags,F.DIF_DROPDOWNLIST) end
  422.  
  423.             elseif typ == "u" then
  424.  
  425.                 item.Type = F.DI_USERCONTROL
  426.  
  427.             else
  428.  
  429.                 item.Type = F.DI_TEXT
  430.  
  431.             end
  432.  
  433.             if opt:find("|") then item.Flags = bor(item.Flags,F.DIF_CENTERTEXT) end
  434.  
  435.             if opt:find(">") then item.Flags = bor(item.Flags,F.DIF_RIGHTTEXT) end
  436.  
  437.             if opt:find("<") then item.Flags = bor(item.Flags,F.DIF_LEFTTEXT) end
  438.  
  439.             if opt:find("!") then item.Flags = bor(item.Flags,F.DIF_DEFAULTBUTTON) end
  440.  
  441.             if opt:find("[=c]") then item.Flags = bor(item.Flags,F.DIF_CENTERGROUP) end
  442.  
  443.             if opt:find("[:g]") then item.Flags = bor(item.Flags,F.DIF_GROUP) end
  444.  
  445.             if opt:find("[?x]") then item.Flags = bor(item.Flags,F.DIF_DISABLE) end
  446.  
  447.             if opt:find("[*s]") then item.Flags = bor(item.Flags,F.DIF_SELECTONENTRY) end
  448.  
  449.             if opt:find("/") then item.Flags = bor(item.Flags,F.DIF_NOFOCUS) end
  450.  
  451.             if opt:find("@") then item.Flags = bor(item.Flags,F.DIF_FOCUS) end
  452.  
  453.             if opt:find("&") then item.Flags = bor(item.Flags,F.DIF_SHOWAMPERSAND) end
  454.  
  455.             x = opt:gsub("[^+]","") -- подсчитываем "+"
  456.  
  457.             if x:len()>0 then item[3] = parent:NewLine(x:len()) end
  458.  
  459.             x = opt:gsub("[^%-]","") -- посчитываем "-"
  460.  
  461.             if x:len()>0 then item[3] = parent:NewLine(-x:len()) end
  462.  
  463.         end
  464.  
  465.  
  466.  
  467.         item.X1 = item.X1 or parent.LC   -- [2]
  468.  
  469.         item.Y1 = item.Y1 or parent.Line -- [3]
  470.  
  471.         item.X2 = item.X2 or parent.RC   -- [4]
  472.  
  473.         if item.SL or item.YS or item.SetLine then item.Y1 = parent:SetLine(item.SL or item.YS or item.SetLine) end
  474.  
  475.         if item.NL or item.YN or item.NewLine then item.Y1 = parent:NewLine(item.NL or item.YN or item.NewLine) end
  476.  
  477.         if item.X2==0 and item.X1>0 then item.X2=item.X1 end -- исправляем X2
  478.  
  479.         item.Y2 = item.Y2 or 0           -- [5]
  480.  
  481.         if item.Y2==0 and item.Y1>0 then item.Y2=item.Y1 end -- исправляем Y2
  482.  
  483.         if item.W then
  484.  
  485.             item.X2 = item.X1>=0 and item.X1+item.W-1 or item.W
  486.  
  487.             item.W = nil -- item.X1>=0 and item.X2-item.X1+1 or item.X2
  488.  
  489.         end
  490.  
  491.  
  492.  
  493.         item.Selected = item.Selected or 0 -- Selected
  494.  
  495.         if type(item.History)=="string" and item.History~="" then -- если есть History
  496.  
  497.             item.Flags = bor(item.Flags,F.DIF_HISTORY)
  498.  
  499.         end
  500.  
  501.         if type(item.Separator)=="string" and item.Separator~="" then -- если есть Mask/Separator
  502.  
  503.             if item.Type==F.DI_TEXT or item.Type==F.DI_VTEXT then
  504.  
  505.                 item.Flags = bor(item.Flags,F.DIF_SEPARATORUSER)
  506.  
  507.             else
  508.  
  509.                 item.Flags = bor(item.Flags,F.DIF_MASKEDIT)
  510.  
  511.             end
  512.  
  513.         end
  514.  
  515.         if item.Type==F.DI_BUTTON or item.Type==F.DI_CHECKBOX or item.Type==F.DI_RADIOBUTTON then
  516.  
  517.             item.Text = item.Text or item.Name -- если не задана надпись на кнопке, берём имя элемента
  518.  
  519.         end
  520.  
  521.  
  522.  
  523.         rawset(self,id,item) -- Добавляем
  524.  
  525.     end
  526.  
  527. end
  528.  
  529.  
  530.  
  531. -- Добавление DlgItem вызовом как функции
  532.  
  533. function DlgItemsClass:__call(...)
  534.  
  535.     DlgItemsClass.__newindex(self,false,...)
  536.  
  537. end
  538.  
  539.  
  540.  
  541. -- Конструктор DlgItemsClass
  542.  
  543. setmetatable( DlgItemsClass, {
  544.  
  545.     __call = function(self,dlg)
  546.  
  547.         return setmetatable({[-2]=false,[-1]=dlg,[0]={}},self)
  548.  
  549.     end
  550.  
  551. })
  552.  
  553.  
  554.  
  555. ----------------------------------------------------------------------------------------------------
  556.  
  557. -- Класс Dialog
  558.  
  559. ----------------------------------------------------------------------------------------------------
  560.  
  561.  
  562.  
  563. local DialogClass = {}
  564.  
  565. DialogClass.__index = DialogClass
  566.  
  567.  
  568.  
  569. -- Подчинённые классы
  570.  
  571. DialogClass.DlgItemClass = DlgItemClass
  572.  
  573. DialogClass.DlgItemsClass = DlgItemsClass
  574.  
  575.  
  576.  
  577. ----------------------------------------------------------------------------------------------------
  578.  
  579. -- Добавить элемент диалога
  580.  
  581. function DialogClass:AddItem(item)
  582.  
  583.     if type(item)=="table" then
  584.  
  585.         self.Items[false] = item
  586.  
  587.         return item
  588.  
  589.     end
  590.  
  591. end
  592.  
  593. -- Добавить элементы диалога из массива
  594.  
  595. function DialogClass:AddItems(items)
  596.  
  597.     if type(items)=="table" then
  598.  
  599.         for n,item in ipairs(items) do
  600.  
  601.             self:AddItem(item)
  602.  
  603.         end
  604.  
  605.     end
  606.  
  607.     return self
  608.  
  609. end
  610.  
  611.  
  612.  
  613. -- Подготовка объекта диалога к новому применению, удаление всех dlgitem
  614.  
  615. -- Если используется рамка, будет добавлен элемент диалога для рамки
  616.  
  617. --[=[
  618.  
  619. function DialogClass:Clear()
  620.  
  621.     self.Items = (self.DlgItemsClass or DlgItemsClass)(D) -- self.Items = setmetatable({[-2]=false,[-1]=self,[0]={}},DlgItemsClass)
  622.  
  623.     self.Guid = ""
  624.  
  625.     self.Handle = 0
  626.  
  627.     self.DlgProc = nil
  628.  
  629.     self.HelpTopic = ""
  630.  
  631.     self.Line = 0
  632.  
  633.     self.MaxLine = 0
  634.  
  635.     self.Ret = 0
  636.  
  637.     return self
  638.  
  639. end --]=]
  640.  
  641.  
  642.  
  643. -- Заполнения общих свойств диалога
  644.  
  645. function DialogClass:SetProperties(Tab,...)
  646.  
  647.     local Guid,X1,Y1,X2,Y2,HelpTopic,Items,Flags,DlgProc,Frame
  648.  
  649.     if type(Tab)=="table" then
  650.  
  651.         for i,v in pairs(Tab) do
  652.  
  653.             if i == "Items" then
  654.  
  655.                 Items = v
  656.  
  657.             else
  658.  
  659.                 self[i] = v
  660.  
  661.             end
  662.  
  663.         end
  664.  
  665.     else
  666.  
  667.         Guid = Tab
  668.  
  669.         X1,Y1,X2,Y2,HelpTopic,Items,Flags,DlgProc = ...
  670.  
  671.     end
  672.  
  673.     self.Guid = Guid or self.Guid or ""
  674.  
  675.     self.X1 = X1 or self.X1 or -1
  676.  
  677.     self.Y1 = Y1 or self.Y1 or -1
  678.  
  679.     self.X2 = X2 or self.X2 or 0
  680.  
  681.     self.Y2 = Y2 or self.Y2 or 0
  682.  
  683.     if self.W then
  684.  
  685.         self.X2 = self.X1>=0 and self.X1+self.W-1 or self.W
  686.  
  687.         self.W = nil -- self.X1>=0 and self.X2-self.X1+1 or self.X2
  688.  
  689.     end
  690.  
  691.     if self.H then
  692.  
  693.         self.Y2 = self.Y1>=0 and self.Y1+self.H-1 or self.H
  694.  
  695.         self.H = nil -- self.Y1>=0 and self.Y2-self.Y1+1 or self.Y2
  696.  
  697.     end
  698.  
  699.     self.LC = self.LC or 1
  700.  
  701.     self.RC = self.RC or ( self.X1>=0 and self.X2-self.X1-1 or self.X2-2 )
  702.  
  703.     self.Line = self.Line or 1
  704.  
  705.     self.MaxLine = self.Y1>=0 and self.Y2-self.Y1-1 or self.Y2-2
  706.  
  707.     self.HelpTopic = HelpTopic or self.HelpTopic
  708.  
  709.     if (self.Frame or self.Title) and not self.Items[-2] then
  710.  
  711.         self.Title = self.Title or ""
  712.  
  713.         self.Frame = self.Frame or 2
  714.  
  715.         self.Items[-2] = self:AddItem {self.Frame==1 and F.DI_SINGLEBOX or F.DI_DOUBLEBOX,3,1,(self.X1>=0 and self.X2-self.X1-3 or self.X2-4),(self.Y1>=0 and self.Y2-self.Y1-1 or self.Y2-2),0,0,0,0,self.Title}
  716.  
  717.         self.Line = 1 -- на уровне рамки (не ниже - для упрощения использования опции +)
  718.  
  719.         self.LC = 5 -- правее рамки через символ
  720.  
  721.         self.RC = self.X1>=0 and self.X2-self.X1-5 or self.X2-6 -- левее рамки через символ
  722.  
  723.     end
  724.  
  725.     self:AddItems(Items)
  726.  
  727.     self.Flags = flags2num(Flags or self.Flags)
  728.  
  729.     self.DlgProc = DlgProc or self.DlgProc
  730.  
  731.     self.Handle = self.Handle or 0
  732.  
  733.     self.Refreshed = self.Refreshed or true
  734.  
  735.     self.Ret = 0
  736.  
  737.     return self
  738.  
  739. end
  740.  
  741. -- Отправка сообщения диалогу
  742.  
  743. function DialogClass:Send(Msg,Param1,Param2)
  744.  
  745.     return far.SendDlgMessage(self.Handle,Msg,Param1,Param2)
  746.  
  747. end
  748.  
  749. -- Аналог far.DialogInit()
  750.  
  751. -- В DlgProc будут переданы два дополнительных параметра
  752.  
  753. function DialogClass:Init(...)
  754.  
  755.     self:SetProperties(...)
  756.  
  757.     self.Handle =
  758.  
  759.         far.DialogInit(
  760.  
  761.             self.Guid,self.X1,self.Y1,self.X2,self.Y2,self.HelpTopic,self.Items,self.Flags,
  762.  
  763.             self.DlgProc and function(hDlg,Msg,Param1,Param2) return self.DlgProc(hDlg,Msg,Param1,Param2,self,self.Items) end
  764.  
  765.         )
  766.  
  767.     return self.Handle
  768.  
  769. end
  770.  
  771. -- Аналог far.DialogRun()
  772.  
  773. function DialogClass:Run()
  774.  
  775.     self.Ret = far.DialogRun(self.Handle)
  776.  
  777.     if self.Refreshed then self:Refresh() end
  778.  
  779.     return self.Ret
  780.  
  781. end
  782.  
  783. -- Аналог far.DialogFree()
  784.  
  785. function DialogClass:Free()
  786.  
  787.     far.DialogFree(self.Handle)
  788.  
  789.     self.Handle = 0
  790.  
  791. end
  792.  
  793. -- Аналог far.Dialog()
  794.  
  795. function DialogClass:Exec(...)
  796.  
  797.     self:Init(...) self:Run() self:Free()
  798.  
  799.     return self.Ret
  800.  
  801. end
  802.  
  803. -- Функциональный запуск D() - то же, что D:Exec()
  804.  
  805. function DialogClass:__call(...)
  806.  
  807.     return self:Exec(...)
  808.  
  809. end
  810.  
  811. -- Установить фокус на элемент диалога
  812.  
  813. function DialogClass:SetFocus(id)
  814.  
  815.     return self:Send(F.DM_SETFOCUS,id,0)
  816.  
  817. end
  818.  
  819. -- Получить id элемента диалога в фокусе
  820.  
  821. function DialogClass:GetFocus()
  822.  
  823.     return self:Send(F.DM_GETFOCUS,0,0)
  824.  
  825. end
  826.  
  827. -- Вкл./выкл. автоматическую перерисовку
  828.  
  829. function DialogClass:AutoRedraw(x)
  830.  
  831.     return self:Send(F.DM_ENABLEREDRAW,log2num(x),0)
  832.  
  833. end
  834.  
  835. -- Перерисовать
  836.  
  837. function DialogClass:Redraw()
  838.  
  839.     return self:Send(F.DM_REDRAW,0,0)
  840.  
  841. end
  842.  
  843. ----------------------------------------------------------------------------------------------------
  844.  
  845. -- Установить текущую строку.
  846.  
  847. -- При отрицательном значении отсчёт от D.Opt.MaxLine или D.MaxLine
  848.  
  849. function DialogClass:SetLine(n)
  850.  
  851.     if n then self.Line = n<0 and self.MaxLine+1+n or n end
  852.  
  853.     return self.Line
  854.  
  855. end
  856.  
  857. DialogClass.SL = DialogClass.SetLine
  858.  
  859. -- Перейти на следующую строку или несколько строк относительно текущей
  860.  
  861. function DialogClass:NewLine(n)
  862.  
  863.     self.Line = self.Line+(n or 1)
  864.  
  865.     return self.Line
  866.  
  867. end
  868.  
  869. DialogClass.NL = DialogClass.NewLine
  870.  
  871. ----------------------------------------------------------------------------------------------------
  872.  
  873. -- Сохранить все поля диалога, имена которых не начинаются с символа подчёркивания, в таблицу по условию.
  874.  
  875. -- Возвращается ссылка на таблицу. Если таблица не задана, создаётся новая.
  876.  
  877. function DialogClass:Save(_tab,condition)
  878.  
  879.     if self.Handle==0 then return end
  880.  
  881.     local tab = type(_tab)=="table" and _tab or {}
  882.  
  883.     for id,item in ipairs(self.Items) do
  884.  
  885.         if item.Name~="" and item.Name:sub(1,1)~="_" then -- если имя не начинается с подчёркивания
  886.  
  887.             local typ = item.Type
  888.  
  889.             if type(condition)=="string" then
  890.  
  891.                 if item[condition] then
  892.  
  893.                     tab[item.Name] = item:GetValue()
  894.  
  895.                 end
  896.  
  897.             elseif type(condition)=="function" then
  898.  
  899.                 if condition(item) then
  900.  
  901.                     tab[item.Name] = item:GetValue()
  902.  
  903.                 end
  904.  
  905.             elseif typ~=F.DI_BUTTON and typ~=F.DI_DOUBLEBOX and typ~=F.DI_SINGLEBOX and typ~=F.DI_USERCONTROL then
  906.  
  907.                 tab[item.Name] = item:GetValue()
  908.  
  909.             end
  910.  
  911.         end
  912.  
  913.     end
  914.  
  915.     return tab
  916.  
  917. end
  918.  
  919. -- Скопировать все информационные поля items в таблицу, по аналогии с Save
  920.  
  921. function DialogClass:Copy(_tab,condition)
  922.  
  923.     local tab = type(_tab)=="table" and _tab or {}
  924.  
  925.     for id,item in ipairs(self.Items) do
  926.  
  927.         if item.Name~="" and item.Name:sub(1,1)~="_" then -- если имя не начинается с подчёркивания
  928.  
  929.             local typ = item.Type
  930.  
  931.             if type(condition)=="string" then
  932.  
  933.                 if item[condition] then
  934.  
  935.                     tab[item.Name] = item.Text
  936.  
  937.                 end
  938.  
  939.             elseif type(condition)=="function" then
  940.  
  941.                 if condition(item) then
  942.  
  943.                     tab[item.Name] = item.Selected
  944.  
  945.                 end
  946.  
  947.             elseif typ~=F.DI_BUTTON and typ~=F.DI_DOUBLEBOX and typ~=F.DI_SINGLEBOX and typ~=F.DI_USERCONTROL then
  948.  
  949.                 tab[item.Name] = item:GetValue()
  950.  
  951.             end
  952.  
  953.         end
  954.  
  955.     end
  956.  
  957.     return tab
  958.  
  959. end
  960.  
  961. -- Обновить все исходные items значениями из диалога
  962.  
  963. function DialogClass:Refresh()
  964.  
  965.     for id,item in ipairs(self.Items) do
  966.  
  967.         local real = far.GetDlgItem(self.Handle,id) -- можно бы сделать setmetatable, но нафиг
  968.  
  969.         if type(item.Selected)=="table" then
  970.  
  971.             item.Selected.SelectIndex = item:GetListPos()
  972.  
  973.         else
  974.  
  975.             item.Selected = real[6]
  976.  
  977.         end
  978.  
  979.         item.Text = real[10]
  980.  
  981.     end
  982.  
  983.  
  984.  
  985. end
  986.  
  987. -- Загрузить все поля диалога из таблицы по условию
  988.  
  989. function DialogClass:Load(tab,condition)
  990.  
  991.     if self.Handle==0 then return end
  992.  
  993.     for id,item in ipairs(self.Items) do
  994.  
  995.         if item.Name~="" and item.Name:sub(1,1)~="_" and tab[item.Name]~=nil then -- если имя не начинается с подчёркивания
  996.  
  997.             if type(condition)=="string" then
  998.  
  999.                 if item[condition] then
  1000.  
  1001.                     item:SetValue(tab[item.Name])
  1002.  
  1003.                 end
  1004.  
  1005.             elseif type(condition)=="function" then
  1006.  
  1007.                 if condition(item) then
  1008.  
  1009.                     item:SetValue(tab[item.Name])
  1010.  
  1011.                 end
  1012.  
  1013.             else
  1014.  
  1015.                 item:SetValue(tab[item.Name])
  1016.  
  1017.             end
  1018.  
  1019.         end
  1020.  
  1021.     end
  1022.  
  1023. end
  1024.  
  1025. ----------------------------------------------------------------------------------------------------
  1026.  
  1027. -- Конструктор
  1028.  
  1029. local function New(...)
  1030.  
  1031.     local D = {}
  1032.  
  1033.     D.Items = DlgItemsClass(D) -- D.Items = setmetatable({[-2]=false,[-1]=D,[0]={}},DlgItemsClass)
  1034.  
  1035.     D.I, D.i = D.Items, D.Items -- синонимы
  1036.  
  1037.     setmetatable(D,DialogClass) -- объект
  1038.  
  1039.     D:SetProperties(...)
  1040.  
  1041.     -- Возвращаемые значения: сам диалог, таблица Items, функция SetLine, функция NewLine
  1042.  
  1043.     return D, D.Items, function(n) return D:SetLine(n) end, function(n) return D:NewLine(n) end
  1044.  
  1045. end
  1046.  
  1047. ----------------------------------------------------------------------------------------------------
  1048.  
  1049. return
  1050.  
  1051.     {
  1052.  
  1053.         New = New,
  1054.  
  1055.         DialogClass=DialogClass,
  1056.  
  1057.         DlgItemClass=DlgItemClass,
  1058.  
  1059.         DlgItemsClass=DlgItemsClass,
  1060.  
  1061.         log2num = log2num,
  1062.  
  1063.         state2num = state2num,
  1064.  
  1065.         flags2num = flags2num,
  1066.  
  1067.         _VERSION = _version
  1068.  
  1069.     }
  1070.  
  1071. ----------------------------------------------------------------------------------------------------
Разум когда-нибудь победит.
User avatar
sToLp
Posts: 938
Joined: Thu 23 Jul, 2009 10:16
Has thanked: 114 times
Been thanked: 12 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by sToLp »

Smitis
Макрос переименования с новым модулем и внесёнными в макрос рекомендованными Вами правками работает хорошо.

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

Например, полезно было бы иметь возможность вызывать диалог 'Копирование', в котором раздельно настраивать путь (пути), имя и расширение файла; и редакторные диалоги 'Открыть/создать файл' и 'Сохранить файл как' — путь, имя, расширение файла + кодовая страница.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Smitis »

sToLp wrote:Конструкции, которые Вы предлагаете, безусловно интересны и полезны для изучения. Но для повседневного использования они, на мой взгляд, весьма сложны.
Удивило это мнение, ведь модуль создавался для облегчения работы с диалогами. Неужели всё настолько сложно (я имею ввиду не внутреннюю структуру, а именно использование)?
Разум когда-нибудь победит.
User avatar
sToLp
Posts: 938
Joined: Thu 23 Jul, 2009 10:16
Has thanked: 114 times
Been thanked: 12 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by sToLp »

Smitis wrote: модуль создавался для облегчения работы с диалогами
Применить макрос, созданный на основе модуля, естественно, не сложно. А вот само создание макросов с использованием модуля представляется мне делом затруднительным, требующим немалого дополнительного напряжения, да. Поэтому я бы предпочёл иметь несколько шаблонных макросов. Но, возможно, это сугубо субъективно.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Smitis »

sToLp wrote:А вот само создание макросов с использованием модуля представляется мне делом затруднительным, требующим немалого дополнительного напряжения, да.
Напряжение менее, чем делать диалог без модуля - вводить меньше.
А один шаблон уже есть - это тот самый RENAME. В нём есть:
- Конфигурация вынесенная в начало скрипта (правда, не в отдельный файл).
- Локализация интерфейса.
- Простая диалоговая процедура.
- Вызов конструктора New.
- Добавление элементов диалога несколькими разными способами.
- Обработка результатов.

Будет ещё что-нибудь интересное - выложу.
Разум когда-нибудь победит.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Smitis »

v.1.10
Описание в шапке пока дорабатываю

Изменения DialogClass:

- Допилены Save, Copy, Load. Я ими почти не пользовался, оказалось в них содержатся какие-то совершенно невнятные моменты.
- Рамка будет иметь имя __MainFrame
- Поле диалога WW (Width of Work area) содержит ширину области для элементов с учётом отступа от краёв и от рамки для удобства задания ширины элементов "во всю ширину".

Изменения DialogItemClass:

- Методы CheckType и CheckFlags (в большей степени для внутреннего использования)
- Поле H для задания высоты DI_LISTBOX, DI_COMBOBOX, DI_USERCONTROL
- Поле W для задания ширины
- Уточнение задаваемых координат по умолчанию для большего соответствия справке
- Сокращения для стандартных полей элемента T=Text, Max=MaxLength, F=Flags, His=History, LI=ListItems, S=Sel=Selected, Sep=Separator
Разум когда-нибудь победит.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Smitis »

Ещё примеры использования http://forum.farmanager.com/viewtopic.p ... 08#p133908
Разум когда-нибудь победит.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Smitis »

v.1.11

- DialogClass:ExistItem
Так как с некоторых пор (v.1.09) обращение по имени к несуществующему элементу вызывает конструктор нового элемента с данным именем, стало невозможным делать проверки на существование элемента (иногда нужно, когда элемент создаётся по условию). Проверить можно через D.Items[0]["имя_элемента"] или rawget или новым методом ExistItem. Метод возвращает либо таблицу элемента (по сути - прямой доступ к таблице через rawget) или nil.
- Доработан flags2num. Изменение не принципиальное, чисто для большей универсальности.
- GUID теперь можно задавать в любом из двух стандартных видов, как бинарную строку, так и в виде строки xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Преобразование в бинарную строку происходит автоматически.
- DlgItemClass:SetFlags(...) для задания флагов. Для удобства. По сути item.Flags = bor(item.Flags,...) c пребразованием флагов в виде строк и таблиц в number.
Разум когда-нибудь победит.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Smitis »

v.1.15
Промежуточные версии не выкладывал, там ничего интересного, экспериментировал.
Изменения:
  • Теперь для каждого элемента можно указывать свой ItemProc. Принимает те же параметры, что и DlgProc, за исключением Param1. Если возвращает "истину", вызывается DlgProc, иначе обработка заканчивается.
  • Свойство Parent переименовано в Owner, чтобы было как в FAR-е.
  • Свойств SL, NL, YS, YN элементов диалога больше нет. Вместо них таблица WorkArea.
  • Удалены методы SetCheckState и GetCheckState как не вписывающиеся в общую концепцию.
    Вроде, ничего не забыл.
    Свойства LC, RC, MaxLine, LastLine на очереди к удалению. Пока не удалены, под вопросом.
Разум когда-нибудь победит.
Shmuel
Posts: 6820
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 528 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by Shmuel »

Smitis,
В связи с тем, что в Code: [Выделить всё] [Expand/Collapse] перестало работать "выделить всё", видимо надо приаттачить файл.
User avatar
HaRT
Moderator
Posts: 10823
Joined: Tue 30 Aug, 2005 17:21
Has thanked: 221 times
Been thanked: 358 times

DialogClass — модуль для работы с диалогами с стиле ООП

Post by HaRT »

Shmuel wrote: Wed 31 Jan, 2024 19:15 видимо надо приаттачить файл
Фар есть инструмент, а не нянька. © 2009 DrKnS
Post Reply

Return to “Обсуждение макросов”