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

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

Post by Smitis »

Latest post of the previous page:

v.1.03
- Исправлен I:GetListPos
- Добавлен метод I:Unchanged - управление состоянием "неизменённый" элемента диалога.
- Методы I:Show, I:Enable при отсутствии параметра теперь возвращают текущее состояние, не меняя его.
- I:Focus теперь возвращает логическое значение.
Разум когда-нибудь победит.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

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

Post by Smitis »

v1.05
Если ВДРУГ кто этим пользуется - предыдущее поведение ломается. Может, даже стоило сделать DialogClass2...

Изменения для DlgItemClass и DlgItemsClass:
- Все флаги, заданные строкой или таблицей, преобразуются в number.
- Новый синоним для поля Mask - Separator.
- Возможность задавать элемент диалога таблицей с именованными полями в виде {Type="DI_TEXT",X1=5,Text="QWERTY"}.
- Недостающие поля по умолчанию заполняются значениями Type=far.Flags.DI_TEXT, X1=-1, X2=0, Y1=D.Line (внутренний счётчик строк объекта DialogClass), Y2=Y1, Selected=0, Flags=0.
- Два поля YN и YS (соответствующие синонимы NL, NewLine и SL, SetLine) служат для управления Y1 с использованием внутреннего счётчика строк (D.Line) и имеют тот же смысл, что и методы SetLine и NewLine диалога. Эти поля удаляются после добавления элемента в D.Items.
- Если поле History не пустая строка, автоматически выставляется флаг DIF_HISTORY.
- Если поле Mask (Separator) не пустая строка, в зависимости от типа элемента автоматически выставляется флаг DIF_MASKEDIT или DIF_SEPARATORUSER.
- Если не задан Type элемента диалога, принимается DI_TEXT.
- Добавлено упрощенное задание типа элементов и некоторых флагов - если тип элемента диалога строка не начинающаяся с "DI_", то первый символ рассматривается как тип элемента, а остальные как уточнения типа элемента или флаги. Заданные таким образом флаги объединяюся по OR с остальными флагами, заданными через Flags.

Изменения для DialogClass:
- Изменение синтаксиса New, Init, Exec и SetProperties. Если первый параметр - таблица, остальные параметры игнорируются. Иначе - все параметры обычный список параметров диалога. Таблица используется для задания параметров диалога. Opt больше не используется, получается на один параметр меньше, что ломает предыдущее поведение.
Пример:
D:Init { Guid=DLG_ID, X2=20, Y2=30, Flags="FDLG_WARNING" }.
Недостающие параметры принимаются: X1=-1, Y1=-1.
- Opt.Refresh -> свойство Refreshed диалога.
- AddItem теперь принимает только один параметр - таблицу с параметрами элемента диалога.
- Функциональный вызов диалога D() то же, что и применение метода D:Exec.
- Функциональный вызов D.Items() то же, что и применение метода D:AddItem.

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

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

Post by Smitis »

Небольшой пример использования.
Макрос для переименования файлов - раздельно имя и расширение.
Разум когда-нибудь победит.
User avatar
John Doe
Бюрократ
Posts: 13851
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 73 times
Been thanked: 428 times
Contact:

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

Post by John Doe »

Smitis wrote:Может, даже стоило сделать DialogClass2...
Незачем, если конечно предыдущий вариант не будет развиваться параллельно.

Возможно ли добавить возможность не указывать координаты элементов (т.е. автоматически рассчитывать их)?
User avatar
HaRT
Moderator
Posts: 10858
Joined: Tue 30 Aug, 2005 17:21
Has thanked: 221 times
Been thanked: 358 times

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

Post by HaRT »

John Doe wrote:Возможно ли добавить возможность не указывать координаты элементов (т.е. автоматически рассчитывать их)?
Мало-мальски общий layout engine — это нетривиальная штука, его с наскоку хорошо не сделаешь.
Фар есть инструмент, а не нянька. © 2009 DrKnS
User avatar
John Doe
Бюрократ
Posts: 13851
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 73 times
Been thanked: 428 times
Contact:

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

Post by John Doe »

Вроде бы фаровский DialogBuilder делает именно это. К сожалению в luafar не реализовано.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

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

Post by Smitis »

John Doe wrote:Незачем, если конечно предыдущий вариант не будет развиваться параллельно.

Возможно ли добавить возможность не указывать координаты элементов (т.е. автоматически рассчитывать их)?
Параллельно развивать не буду, дополнительная таблица параметров оказалась совершенно лишняя. А сейчас получается, имхо, вполне в духе lua, точнее, в духе "декларативного" синтаксиса.

С расчётом координат - пока не придумал. То что есть, всякие там NewLine и пр. как раз для полуавтоматического расчёта строк (большинство диалогов, мне кажется, имеет вертикальную структуру). Надо начать со следующей строки - достаточно вместо Y1 указать (в новой версии) YN=1. Через одну YN=2. Если это несколько кнопок на одной строке, Y1 для последующих уже не указывать. Если вдруг понадобилось расширить диалог, строки пересчитаются автоматически. Не хватает, пока, автоматического определения высоты диалога.
По горизонтали хуже. Например, для DI_TEXT ширина расчитывается ФАРом автоматически, значение изначально может быть и не задано, где размещать следующий элемент непонятно. Но хочу добавить что-то типа параметра XL или Width - если задана таким образом "ширина" элемента, изменяем внутренний счётчик, и если у следующего элемента в той же строке не задан X1, подставлять это значение. Со следующей строки счётчик обнуляем.

Если есть идеи - давайте.
Разум когда-нибудь победит.
User avatar
HaRT
Moderator
Posts: 10858
Joined: Tue 30 Aug, 2005 17:21
Has thanked: 221 times
Been thanked: 358 times

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

Post by HaRT »

Smitis wrote:Если есть идеи - давайте.
Есть идея методологического свойства: прежде, чем изобретать собственный велосипед, посмотреть, нельзя ли взять готовый — в плане возможностей, общности, архитектуры и т.п.
Фар есть инструмент, а не нянька. © 2009 DrKnS
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

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

Post by Smitis »

Готовый велосипед - это DialogBuilder?
Разум когда-нибудь победит.
User avatar
HaRT
Moderator
Posts: 10858
Joined: Tue 30 Aug, 2005 17:21
Has thanked: 221 times
Been thanked: 358 times

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

Post by HaRT »

Smitis wrote:Готовый велосипед - это DialogBuilder?
Возможно, я его не смотрел.
Но есть большое количество фреймворков для создания оконного пользовательского интерфейса (включая диапазон от Borland TurboVision через Trolltech–Nokia–Digia Qt до Microsoft WPF), и в каждом есть свой layout engine. Можно выбрать тот, который больше похож на желаемое, и позаимствовать идеи, способы описания, логику работы, реализацию и пр. Наверняка есть и что-то гораздо более простое в заимствовании и использовании.
Фар есть инструмент, а не нянька. © 2009 DrKnS
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

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

Post by Smitis »

HaRT wrote:Но есть большое количество фреймворков для создания оконного пользовательского интерфейса (включая диапазон от Borland TurboVision через Trolltech–Nokia–Digia Qt до Microsoft WPF), и в каждом есть свой layout engine. Можно выбрать тот, который больше похож на желаемое, и позаимствовать идеи, способы описания, логику работы, реализацию и пр. Наверняка есть и что-то гораздо более простое в заимствовании и использовании.
:-o
а потом так
:skull:
шютка
Разум когда-нибудь победит.
User avatar
John Doe
Бюрократ
Posts: 13851
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 73 times
Been thanked: 428 times
Contact:

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

Post by John Doe »

Smitis wrote: Небольшой пример использования.
А используете ли вы 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 »

John Doe wrote:А используете ли вы DialogClass в каких-то других проектах?
Обычно как в примере, когда надо быстро набросать диалог.
Самый крупный проект, использующий DialogClass был редактор ID3 тегов, но, увы, до конца не доведён. :(
Разум когда-нибудь победит.
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.07 (1.06 как-то потерялась)

Пока не правлю первый пост, экспериментирую.

- Изменения для диалога:
- Refreshed теперь по умолчанию true.
- Переменные LC и RC задающие левый и правый столбец для значений по умолчанию X1 и X2 элементов.
- Поле W задаёт ширину диалога. При этом, если X1>=0, то X2=X1+W-1, иначе (центрирование) X2=W.
- Поле H задаёт высоту диалога. При этом, если Y1>=0, то Y2=Y1+H-1, иначе (центрирование) Y2=H.
- Поля Frame и Title задают рамку с заголовком, аналогичную стандартным диалогам ФАРа.
Для рисования рамки к элементам диалога добавляется элемент типа DI_DOUBLEBOX или DI_SINGLEBOX.
Если Frame==1, рисуется одинарная рамка, иначе двойная.
Если задан Title, но не задан Frame, рисуется двойная рамка.
Всё это делается новым методом диалога AddMainFrame(frame,title).
Рекомендуется задавать Frame и Title в конструкторе диалога New, вмести с параметрами, задающими размер диалога. Тогда, во-первых, будут правильные переменные типа Line, LC, RC. Во-вторых, элемент для рамки добавится первым и будет правильно определяться Object.Title.

- Изменения для элемента диалога:
- Если надпись Text для кнопки/радио/чекбокса не задана, будет взято имя поля Name.
- Буквенные опции для полей ввода "e"
h = DIF_HISTORY
m = DIF_MANUALADDHISTORY
l = DIF_USELASTHISTORY
- X1 по умолчанию = D.LC
- X2 по умолчанию = D.RC
- Y1 по умолчанию = D.Line
- Поле W задаёт ширину элемента. X2 считается автоматически.
  1. --[=[ ----------------------------------------------------------------------------------------------
  2.  
  3. Модуль для работы с диалогами в стиле ООП.
  4.  
  5. v. 1.07 (C) 2013-2015 Smitis
  6.  
  7. viewtopic.php?t=8523
  8.  
  9. --]=] ----------------------------------------------------------------------------------------------
  10.  
  11.  
  12.  
  13. local _version = "1.07"
  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 -- Добавлена рамка
  276.  
  277. ----------------------------------------------------------------------------------------------------
  278.  
  279.  
  280.  
  281. local DlgItemsClass = {}
  282.  
  283. function DlgItemsClass:__index(name)
  284.  
  285.     return rawget(self, self[0][name] or name)
  286.  
  287. end
  288.  
  289.  
  290.  
  291. -- Типы наборов для разделителей
  292.  
  293. --local sep_h = { "├─┤", "╞═╡", "╟─╢", "╠═╣" }
  294.  
  295. --local sep_v = { "┬│┴", "╥║╨", "╤│╧", "╦║╩" }
  296.  
  297.  
  298.  
  299. -- Добавление нового элемента диалога
  300.  
  301. function DlgItemsClass:__newindex(name,item)
  302.  
  303.     local x
  304.  
  305.     if type(item)=="table" then
  306.  
  307.         local id = #self+1
  308.  
  309.         local parent = self[-1]
  310.  
  311.         item.Name = name or item.Name or "_"
  312.  
  313.         item.Id = id
  314.  
  315.         item.Parent = parent
  316.  
  317.         self[0][item.Name] = id -- в список именованных элементов
  318.  
  319.         -- копируем все виртуальные поля в числовые и удаляем их
  320.  
  321.         for f,v in pairs(item) do
  322.  
  323.             if DlgItemClass.NamedFields[f] then
  324.  
  325.                 item[DlgItemClass.NamedFields[f]] = v
  326.  
  327.                 item[f] = nil
  328.  
  329.             end
  330.  
  331.         end
  332.  
  333.        
  334.  
  335.         setmetatable(item,parent.DlgItemClass or DlgItemClass) -- объект
  336.  
  337.  
  338.  
  339.         item.Flags = flags2num(item.Flags) -- Flags [9] в number
  340.  
  341.         if type(item.Type)=="number" or item.Type:sub(1,3)=="DI_" then
  342.  
  343.             item.Type = item.Type and flags2num(item.Type) or F.DI_TEXT -- тип элемента в number или тип по умолчанию
  344.  
  345.         else -- Если тип - строка начинающаяся не с DI_, это сокращённое обозначение типа элемента, параметров, опций
  346.  
  347.             local typ = item.Type:sub(1,1)
  348.  
  349.             local opt = item.Type:sub(2)
  350.  
  351.             if typ == "b" then
  352.  
  353.                 item.Type = F.DI_BUTTON
  354.  
  355.                 if opt:find("b") then item.Flags = bor(item.Flags,F.DIF_NOBRACKETS) end
  356.  
  357.                 if opt:find(";") then item.Flags = bor(item.Flags,F.DIF_BTNNOCLOSE) end
  358.  
  359.             elseif typ == "f" then
  360.  
  361.                 item.Type = opt:find("d") and F.DI_DOUBLEBOX or F.DI_SINGLEBOX
  362.  
  363.                 --if opt:find("m") end -- MainFrame ???
  364.  
  365.             elseif typ == "t" then
  366.  
  367.                 item.Type = opt:find("v") and F.DI_VTEXT or F.DI_TEXT
  368.  
  369.                 -- DIF_WORDWRAP ???
  370.  
  371.             elseif typ == "e" then
  372.  
  373.                 item.Type = opt:find("f") and F.DI_FIXEDIT or F.DI_EDIT
  374.  
  375.                 if opt:find("o") then item.Flags = bor(item.Flags,F.DIF_READONLY) end
  376.  
  377.                 if opt:find("e") then item.Flags = bor(item.Flags,F.DIF_EDITOR) end
  378.  
  379.                 if opt:find("h") then item.Flags = bor(item.Flags,F.DIF_HISTORY) end
  380.  
  381.                 if opt:find("m") then item.Flags = bor(item.Flags,F.DIF_MANUALADDHISTORY) end
  382.  
  383.                 if opt:find("l") then item.Flags = bor(item.Flags,F.DIF_USELASTHISTORY) end
  384.  
  385.             elseif typ == "s" then -- DI_TEXT | DI_VTEXT & DIF_SEPARATOR | DIF_SEPARATOR2
  386.  
  387.                 item.Type = opt:find("v") and F.DI_VTEXT or F.DI_TEXT
  388.  
  389.                 item.Flags = bor(item.Flags,opt:find("d") and F.DIF_SEPARATOR2 or F.DIF_SEPARATOR)
  390.  
  391.             elseif typ == "r" then
  392.  
  393.                 item.Type = F.DI_RADIOBUTTON
  394.  
  395.             elseif typ == "c" then
  396.  
  397.                 item.Type = F.DI_CHECKBOX
  398.  
  399.                 if opt:find("3") then item.Flags = bor(item.Flags,F.DIF_3STATE) end
  400.  
  401.             elseif typ == "l" then
  402.  
  403.                 item.Type = F.DI_LISTBOX
  404.  
  405.             elseif typ == "x" then
  406.  
  407.                 item.Type = F.DI_COMBOBOX
  408.  
  409.                 if opt:find("d") then item.Flags = bor(item.Flags,F.DIF_DROPDOWNLIST) end
  410.  
  411.             end
  412.  
  413.             if opt:find("|") then item.Flags = bor(item.Flags,F.DIF_CENTERTEXT) end
  414.  
  415.             if opt:find(">") then item.Flags = bor(item.Flags,F.DIF_RIGHTTEXT) end
  416.  
  417.             if opt:find("<") then item.Flags = bor(item.Flags,F.DIF_LEFTTEXT) end
  418.  
  419.             if opt:find("!") then item.Flags = bor(item.Flags,F.DIF_DEFAULTBUTTON) end
  420.  
  421.             if opt:find("[=c]") then item.Flags = bor(item.Flags,F.DIF_CENTERGROUP) end
  422.  
  423.             if opt:find("[:g]") then item.Flags = bor(item.Flags,F.DIF_GROUP) end
  424.  
  425.             if opt:find("[?x]") then item.Flags = bor(item.Flags,F.DIF_DISABLE) end
  426.  
  427.             if opt:find("/") then item.Flags = bor(item.Flags,F.DIF_NOFOCUS) end
  428.  
  429.             if opt:find("@") then item.Flags = bor(item.Flags,F.DIF_FOCUS) end
  430.  
  431.             if opt:find("&") then item.Flags = bor(item.Flags,F.DIF_SHOWAMPERSAND) end
  432.  
  433.             x = opt:gsub("[^+]","") -- подсчитываем "+"
  434.  
  435.             if x:len()>0 then item[3] = parent:NewLine(x:len()) end
  436.  
  437.             x = opt:gsub("[^%-]","") -- посчитываем "-"
  438.  
  439.             if x:len()>0 then item[3] = parent:NewLine(-x:len()) end
  440.  
  441.         end
  442.  
  443.  
  444.  
  445.         -- X,Y
  446.  
  447.         item.X1 = item.X1 or parent.LC   -- [2]
  448.  
  449.         item.Y1 = item.Y1 or parent.Line -- [3]
  450.  
  451.         item.X2 = item.X2 or parent.RC   -- [4]
  452.  
  453.         if item.SL or item.YS or item.SetLine then item.Y1 = parent:SetLine(item.SL or item.YS or item.SetLine) end -- Y1
  454.  
  455.         if item.NL or item.YN or item.NewLine then item.Y1 = parent:NewLine(item.NL or item.YN or item.NewLine) end -- Y1
  456.  
  457.         if item.X2==0 and item.X1>0 then item.X2=item.X1 end -- исправляем X2
  458.  
  459.         item.Y2 = item.Y2 or 0           -- [5]
  460.  
  461.         if item.Y2==0 and item.Y1>0 then item.Y2=item.Y1 end -- исправляем Y2
  462.  
  463.         if item.W then
  464.  
  465.             item.X2 = item.X1>=0 and item.X1+item.W-1 or item.W
  466.  
  467.             item.W = nil -- или лучше пересчитывать, как в диалоге ???
  468.  
  469.         end
  470.  
  471.        
  472.  
  473.         item.Selected = item.Selected or 0 -- Selected
  474.  
  475.         if type(item.History)=="string" and item.History~="" then item.Flags = bor(item.Flags,F.DIF_HISTORY) end -- если есть History
  476.  
  477.         if type(item.Separator)=="string" and item.Separator~="" then -- если есть Mask/Separator
  478.  
  479.             if item.Type==F.DI_TEXT or item.Type==F.DI_VTEXT then
  480.  
  481.                 item.Flags = bor(item.Flags,F.DIF_SEPARATORUSER)
  482.  
  483.             else
  484.  
  485.                 item.Flags = bor(item.Flags,F.DIF_MASKEDIT)
  486.  
  487.             end
  488.  
  489.         end
  490.  
  491.         if item.Type==F.DI_BUTTON or item.Type==F.DI_CHECKBOX or item.Type==F.DI_RADIOBUTTON then
  492.  
  493.             item.Text = item.Text or item.Name -- если не задана надпись на кнопке, берём имя элемента      
  494.  
  495.         end
  496.  
  497.        
  498.  
  499.         rawset(self,id,item) -- Добавляем
  500.  
  501.     end
  502.  
  503. end
  504.  
  505.  
  506.  
  507. -- Добавление DlgItem вызовом как функции
  508.  
  509. function DlgItemsClass:__call(...)
  510.  
  511.     DlgItemsClass.__newindex(self,false,...)
  512.  
  513. end
  514.  
  515.  
  516.  
  517. -- Конструктор DlgItemsClass
  518.  
  519. setmetatable( DlgItemsClass, {
  520.  
  521.     __call = function(self,dlg)
  522.  
  523.         return setmetatable({[-2]=false,[-1]=dlg,[0]={}},self)
  524.  
  525.     end
  526.  
  527. })
  528.  
  529.  
  530.  
  531. ----------------------------------------------------------------------------------------------------
  532.  
  533. -- Класс Dialog
  534.  
  535. ----------------------------------------------------------------------------------------------------
  536.  
  537.  
  538.  
  539. local DialogClass = {}
  540.  
  541. DialogClass.__index = DialogClass
  542.  
  543.  
  544.  
  545. -- Подчинённые классы
  546.  
  547. DialogClass.DlgItemClass = DlgItemClass
  548.  
  549. DialogClass.DlgItemsClass = DlgItemsClass
  550.  
  551.  
  552.  
  553. ----------------------------------------------------------------------------------------------------
  554.  
  555. -- Добавить элемент диалога
  556.  
  557. function DialogClass:AddItem(item)
  558.  
  559.     if type(item)=="table" then
  560.  
  561.         self.Items[false] = item
  562.  
  563.         return item
  564.  
  565.     end
  566.  
  567. end
  568.  
  569. -- Добавить элементы диалога из массива
  570.  
  571. function DialogClass:AddItems(items)
  572.  
  573.     if type(items)=="table" then
  574.  
  575.         for n,item in ipairs(items) do
  576.  
  577.             self:AddItem(item)
  578.  
  579.         end
  580.  
  581.     end
  582.  
  583.     return self
  584.  
  585. end
  586.  
  587. -- Добавить рамку диалога
  588.  
  589. function DialogClass:AddMainFrame(frame,title)
  590.  
  591.     if frame or title then
  592.  
  593.         if not self.Items[-2] then
  594.  
  595.             self.Title = title or self.Title or ""
  596.  
  597.             self.Frame = frame or self.Frame or 2
  598.  
  599.             frame = self.Frame==1 and F.DI_SINGLEBOX or F.DI_DOUBLEBOX
  600.  
  601.             self:AddItem {frame,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}
  602.  
  603.             self.Items[-2] = true
  604.  
  605.             self.Line = 1 -- на уровне рамки (не ниже - для упрощения использования опции +)
  606.  
  607.             self.LC = 5 -- правее рамки через символ
  608.  
  609.             self.RC = self.X1>=0 and self.X2-self.X1-5 or self.X2-6 -- левее рамки через символ
  610.  
  611.         end
  612.  
  613.     end
  614.  
  615. end
  616.  
  617. -- Подготовка объекта диалога к новому применению, удаление всех dlgitem
  618.  
  619. -- Если используется рамка, будет добавлен элемент диалога для рамки
  620.  
  621. function DialogClass:Clear()
  622.  
  623.     self.Items = (self.DlgItemsClass or DlgItemsClass)(D) -- self.Items = setmetatable({[-2]=false,[-1]=self,[0]={}},DlgItemsClass)
  624.  
  625.     self.Guid = ""
  626.  
  627.     self.Handle = 0
  628.  
  629.     self.DlgProc = nil
  630.  
  631.     self.HelpTopic = ""
  632.  
  633.     self.Line = 0
  634.  
  635.     self.MaxLine = 0
  636.  
  637.     self:AddMainFrame(self.Frame,self.Title)
  638.  
  639.     self.Ret = 0
  640.  
  641.     return self
  642.  
  643. end
  644.  
  645. -- Заполнения общих свойств диалога
  646.  
  647. function DialogClass:SetProperties(Tab,...)
  648.  
  649.     local Guid,X1,Y1,X2,Y2,HelpTopic,Items,Flags,DlgProc,Frame
  650.  
  651.     if type(Tab)=="table" then
  652.  
  653.         for i,v in pairs(Tab) do
  654.  
  655.             if i == "Items" then
  656.  
  657.                 Items = v
  658.  
  659.             else
  660.  
  661.                 self[i] = v
  662.  
  663.             end
  664.  
  665.         end
  666.  
  667.     else
  668.  
  669.         Guid = Tab
  670.  
  671.         X1,Y1,X2,Y2,HelpTopic,Items,Flags,DlgProc = ...
  672.  
  673.     end
  674.  
  675.     self.Guid = Guid or self.Guid or ""
  676.  
  677.     self.X1 = X1 or self.X1 or -1
  678.  
  679.     self.Y1 = Y1 or self.Y1 or -1
  680.  
  681.     self.X2 = X2 or self.X2 or 0
  682.  
  683.     self.Y2 = Y2 or self.Y2 or 0
  684.  
  685.     if self.W then self.X2 = self.X1>=0 and self.X1+self.W-1 or self.W end
  686.  
  687.     if self.H then self.Y2 = self.Y1>=0 and self.Y1+self.H-1 or self.H end
  688.  
  689.     self.LC = self.LC or 1
  690.  
  691.     self.RC = self.RC or ( self.X1>=0 and self.X2-self.X1-1 or self.X2-2 )
  692.  
  693.     self.Line = self.Line or 1
  694.  
  695.     self.MaxLine = self.Y1>=0 and self.Y2-self.Y1-1 or self.Y2-2
  696.  
  697.     self.HelpTopic = HelpTopic or self.HelpTopic
  698.  
  699.     self:AddMainFrame(self.Frame,self.Title)
  700.  
  701.     self:AddItems(Items)
  702.  
  703.     self.W = self.X1>=0 and self.X2-self.X1+1 or self.X2 -- даже если было задано, заново пересчитываем
  704.  
  705.     self.H = self.Y1>=0 and self.Y2-self.Y1+1 or self.Y2 -- даже если было задано, заново пересчитываем
  706.  
  707.     self.Flags = flags2num(Flags or self.Flags)
  708.  
  709.     self.DlgProc = DlgProc or self.DlgProc
  710.  
  711.     self.Handle = self.Handle or 0
  712.  
  713.     self.Refreshed = self.Refreshed or true
  714.  
  715.     self.Ret = 0
  716.  
  717.     return self
  718.  
  719. end
  720.  
  721. -- Отправка сообщения диалогу
  722.  
  723. function DialogClass:Send(Msg,Param1,Param2)
  724.  
  725.     return far.SendDlgMessage(self.Handle,Msg,Param1,Param2)
  726.  
  727. end
  728.  
  729. -- Аналог far.DialogInit()
  730.  
  731. -- В DlgProc будут переданы два дополнительных параметра
  732.  
  733. function DialogClass:Init(...)
  734.  
  735.     self:SetProperties(...)
  736.  
  737.     self.Handle =
  738.  
  739.         far.DialogInit(
  740.  
  741.             self.Guid,self.X1,self.Y1,self.X2,self.Y2,self.HelpTopic,self.Items,self.Flags,
  742.  
  743.             self.DlgProc and function(hDlg,Msg,Param1,Param2) return self.DlgProc(hDlg,Msg,Param1,Param2,self,self.Items) end
  744.  
  745.         )
  746.  
  747.     return self.Handle
  748.  
  749. end
  750.  
  751. -- Аналог far.DialogRun()
  752.  
  753. function DialogClass:Run()
  754.  
  755.     self.Ret = far.DialogRun(self.Handle)
  756.  
  757.     if self.Refreshed then self:Refresh() end
  758.  
  759.     return self.Ret
  760.  
  761. end
  762.  
  763. -- Аналог far.DialogFree()
  764.  
  765. function DialogClass:Free()
  766.  
  767.     far.DialogFree(self.Handle)
  768.  
  769.     self.Handle = 0
  770.  
  771. end
  772.  
  773. -- Аналог far.Dialog()
  774.  
  775. function DialogClass:Exec(...)
  776.  
  777.     self:Init(...) self:Run() self:Free()
  778.  
  779.     return self.Ret
  780.  
  781. end
  782.  
  783. -- Функциональный запуск D() - то же, что D:Exec()
  784.  
  785. function DialogClass:__call(...)
  786.  
  787.     return self:Exec(...)
  788.  
  789. end
  790.  
  791. -- Установить фокус на элемент диалога
  792.  
  793. function DialogClass:SetFocus(id)
  794.  
  795.     return self:Send(F.DM_SETFOCUS,id,0)
  796.  
  797. end
  798.  
  799. -- Получить id элемента диалога в фокусе
  800.  
  801. function DialogClass:GetFocus()
  802.  
  803.     return self:Send(F.DM_GETFOCUS,0,0)
  804.  
  805. end
  806.  
  807. -- Вкл./выкл. автоматическую перерисовку
  808.  
  809. function DialogClass:AutoRedraw(x)
  810.  
  811.     return self:Send(F.DM_ENABLEREDRAW,log2num(x),0)
  812.  
  813. end
  814.  
  815. -- Перерисовать
  816.  
  817. function DialogClass:Redraw()
  818.  
  819.     return self:Send(F.DM_REDRAW,0,0)
  820.  
  821. end
  822.  
  823. ----------------------------------------------------------------------------------------------------
  824.  
  825. -- Установить текущую строку.
  826.  
  827. -- При отрицательном значении отсчёт от D.Opt.MaxLine или D.MaxLine
  828.  
  829. function DialogClass:SetLine(n)
  830.  
  831.     if n then self.Line = n<0 and self.MaxLine+1+n or n end
  832.  
  833.     return self.Line
  834.  
  835. end
  836.  
  837. DialogClass.SL = DialogClass.SetLine
  838.  
  839. -- Перейти на следующую строку или несколько строк относительно текущей
  840.  
  841. function DialogClass:NewLine(n)
  842.  
  843.     self.Line = self.Line+(n or 1)
  844.  
  845.     return self.Line
  846.  
  847. end
  848.  
  849. DialogClass.NL = DialogClass.NewLine
  850.  
  851. ----------------------------------------------------------------------------------------------------
  852.  
  853. -- Сохранить все поля диалога, имена которых не начинаются с символа подчёркивания, в таблицу по условию.
  854.  
  855. -- Возвращается ссылка на таблицу. Если таблица не задана, создаётся новая.
  856.  
  857. function DialogClass:Save(_tab,condition)
  858.  
  859.     if self.Handle==0 then return end
  860.  
  861.     local tab = type(_tab)=="table" and _tab or {}
  862.  
  863.     for id,item in ipairs(self.Items) do
  864.  
  865.         if item.Name~="" and item.Name:sub(1,1)~="_" then -- если имя не начинается с подчёркивания
  866.  
  867.             local typ = item.Type
  868.  
  869.             if type(condition)=="string" then
  870.  
  871.                 if item[condition] then
  872.  
  873.                     tab[item.Name] = item:GetValue()
  874.  
  875.                 end
  876.  
  877.             elseif type(condition)=="function" then
  878.  
  879.                 if condition(item) then
  880.  
  881.                     tab[item.Name] = item:GetValue()
  882.  
  883.                 end
  884.  
  885.             elseif typ~=F.DI_BUTTON and typ~=F.DI_DOUBLEBOX and typ~=F.DI_SINGLEBOX and typ~=F.DI_USERCONTROL then
  886.  
  887.                 tab[item.Name] = item:GetValue()
  888.  
  889.             end
  890.  
  891.         end
  892.  
  893.     end
  894.  
  895.     return tab
  896.  
  897. end
  898.  
  899. -- Скопировать все информационные поля items в таблицу, по аналогии с Save
  900.  
  901. function DialogClass:Copy(_tab,condition)
  902.  
  903.     local tab = type(_tab)=="table" and _tab or {}
  904.  
  905.     for id,item in ipairs(self.Items) do
  906.  
  907.         if item.Name~="" and item.Name:sub(1,1)~="_" then -- если имя не начинается с подчёркивания
  908.  
  909.             local typ = item.Type
  910.  
  911.             if type(condition)=="string" then
  912.  
  913.                 if item[condition] then
  914.  
  915.                     tab[item.Name] = item.Text
  916.  
  917.                 end
  918.  
  919.             elseif type(condition)=="function" then
  920.  
  921.                 if condition(item) then
  922.  
  923.                     tab[item.Name] = item.Selected
  924.  
  925.                 end
  926.  
  927.             elseif typ~=F.DI_BUTTON and typ~=F.DI_DOUBLEBOX and typ~=F.DI_SINGLEBOX and typ~=F.DI_USERCONTROL then
  928.  
  929.                 tab[item.Name] = item:GetValue()
  930.  
  931.             end
  932.  
  933.         end
  934.  
  935.     end
  936.  
  937.     return tab
  938.  
  939. end
  940.  
  941. -- Обновить все исходные items значениями из диалога
  942.  
  943. function DialogClass:Refresh()
  944.  
  945.     for id,item in ipairs(self.Items) do
  946.  
  947.         local real = far.GetDlgItem(self.Handle,id) -- можно бы сделать setmetatable, но нафиг
  948.  
  949.         if type(item.Selected)=="table" then
  950.  
  951.             item.Selected.SelectIndex = item:GetListPos()
  952.  
  953.         else
  954.  
  955.             item.Selected = real[6]
  956.  
  957.         end
  958.  
  959.         item.Text = real[10]
  960.  
  961.     end
  962.  
  963.  
  964.  
  965. end
  966.  
  967. -- Загрузить все поля диалога из таблицы по условию
  968.  
  969. function DialogClass:Load(tab,condition)
  970.  
  971.     if self.Handle==0 then return end
  972.  
  973.     for id,item in ipairs(self.Items) do
  974.  
  975.         if item.Name~="" and item.Name:sub(1,1)~="_" and tab[item.Name]~=nil then -- если имя не начинается с подчёркивания
  976.  
  977.             if type(condition)=="string" then
  978.  
  979.                 if item[condition] then
  980.  
  981.                     item:SetValue(tab[item.Name])
  982.  
  983.                 end
  984.  
  985.             elseif type(condition)=="function" then
  986.  
  987.                 if condition(item) then
  988.  
  989.                     item:SetValue(tab[item.Name])
  990.  
  991.                 end
  992.  
  993.             else
  994.  
  995.                 item:SetValue(tab[item.Name])
  996.  
  997.             end
  998.  
  999.         end
  1000.  
  1001.     end
  1002.  
  1003. end
  1004.  
  1005. ----------------------------------------------------------------------------------------------------
  1006.  
  1007. -- Конструктор
  1008.  
  1009. local function New(...)
  1010.  
  1011.     local D = {}
  1012.  
  1013.     D.Items = DlgItemsClass(D) -- D.Items = setmetatable({[-2]=false,[-1]=D,[0]={}},DlgItemsClass)
  1014.  
  1015.     D.I, D.i = D.Items, D.Items -- синонимы
  1016.  
  1017.     setmetatable(D,DialogClass) -- объект
  1018.  
  1019.     D:SetProperties(...)
  1020.  
  1021.     -- Возвращаемые значения: сам диалог, таблица Items, функция SetLine, функция NewLine
  1022.  
  1023.     return D, D.Items, function(n) return D:SetLine(n) end, function(n) return D:NewLine(n) end
  1024.  
  1025. end
  1026.  
  1027. ----------------------------------------------------------------------------------------------------
  1028.  
  1029. return
  1030.  
  1031.     {
  1032.  
  1033.         New = New,
  1034.  
  1035.         DialogClass=DialogClass,
  1036.  
  1037.         DlgItemClass=DlgItemClass,
  1038.  
  1039.         DlgItemsClass=DlgItemsClass,
  1040.  
  1041.         log2num = log2num,
  1042.  
  1043.         state2num = state2num,
  1044.  
  1045.         flags2num = flags2num,
  1046.  
  1047.         _VERSION = _version
  1048.  
  1049.     }
  1050.  
  1051. ----------------------------------------------------------------------------------------------------
Разум когда-нибудь победит.
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 ... 91#p132691
Разум когда-нибудь победит.
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 »

Приводимый выше пример макроса ReName выявил некоторую несовместимость с новой версией модуля.
Вот совместимая
Если кто-то использует - макрос был написан для тестирования (хотя оказался полезен). Можно выкинуть варианты вызова, оставив только один, например
Разум когда-нибудь победит.
Post Reply

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