Правильное ведение структуры проекта?
Модератор: Модераторы
Правильное ведение структуры проекта?
Вот уже программирую (самоучка) больше 15 лет но никак не могу научится правильному введению структуры проекта(
Покажите пожалуйста ваши варианты ведения структуры проекта, просто у меня всегда получается каша в которой потом сложно даже самому разобраться)
Если можно видео урок, скрин, или расписать как это же все таки правильно делать.
Имею введу папки, unit, class, функции, структуры и т.п
В интернете этого ничего нет(
Покажите пожалуйста ваши варианты ведения структуры проекта, просто у меня всегда получается каша в которой потом сложно даже самому разобраться)
Если можно видео урок, скрин, или расписать как это же все таки правильно делать.
Имею введу папки, unit, class, функции, структуры и т.п
В интернете этого ничего нет(
- serbod
- постоялец
- Сообщения: 449
- Зарегистрирован: 16.09.2016 10:03:02
- Откуда: Минск
- Контактная информация:
Это такое дело.. Пока новичок, оно нафиг не надо. А когда делаешь серьезные вещи, оно само приходит.
Есть официальный Object Pascal Style Guide - начни с него.
Есть официальный Object Pascal Style Guide - начни с него.
serbod писал(а):Это такое дело.. Пока новичок, оно нафиг не надо. А когда делаешь серьезные вещи, оно само приходит.
Есть официальный Object Pascal Style Guide - начни с него.
Спасибо большое ! А то надоело уже просто быдлокодить)) Сори за ворожение)
Добавлено спустя 25 минут 7 секунд:
Прочитал от начало и до конца но это я соблюдаю с самого начала а что нет ctrl+D
Вообщем как я понимаю нет никаких стандартов по введению проекта, под каждый проект своя логика .
-
LearnMagic
- новенький
- Сообщения: 66
- Зарегистрирован: 10.11.2016 22:13:38
Вот например я так пишу всегда, Это пример главного Unit.
Достаточно понятен код или стоит изменить свой стиль написания кода?
Добавлено спустя 21 секунду:
Спасибо читаю!
Добавлено спустя 43 минуты 33 секунды:
Еще вопросом задался а можно ли обработчики визуальных компонентов главной формы перенести в отдельный unit?
Вот например в JavaScript Можно перехватить обработчик в pascal интересно можно это сделать?)
Я к тому что бы например все обработчики форм перенести в 1 unit
Указатели на функции думаю грамотка получится(
Может как то в классе можно указать procedure TForm1.Button1Click(Sender: TObject); overload; В другом Unit_555
Достаточно понятен код или стоит изменить свой стиль написания кода?
Код: Выделить всё
{ TForm1 }
//Откроем проект через диалог - Вызываем функцию Вывод каталога в TreeView1
procedure TForm1.ToolButton1Click(Sender: TObject);
begin
MenuItem12.Click;
end;
//Сохранить ToolBar1
procedure TForm1.ToolButton3Click(Sender: TObject);
begin
MenuItem13.Click;
end;
//Поиск ToolButton4
procedure TForm1.ToolButton4Click(Sender: TObject);
begin
MenuItem6.Click;
end;
//Кнопка собрать на ToolBar1
procedure TForm1.ToolButton5Click(Sender: TObject);
begin
MenuItem33.Click;
end;
//Дизайнер формы добавить имя компонента в TreeView2 при создание компонента
procedure TForm1.ToolButton6Click(Sender: TObject);
begin
Decorat.DecoratorCreateComponent('Edit');
TreeView2.Items.AddChild(TreeView2.Items.Item[0],
'Edit' + IntToStr(TreeView2.Items.Item[0].Count)).ImageIndex := 2394;
end;
//Отмена ToolBar1
procedure TForm1.ToolButton7Click(Sender: TObject);
begin
MenuItem37.Click;
end;
//Вернуть ToolBar1
procedure TForm1.ToolButton8Click(Sender: TObject);
begin
MenuItem38.Click;
end;
//При создание фоормы
procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffered := True;
//Загрузим настроки из INI приложения при старте программы
Form1.Width := StrToInt(INIRead('Otions.ini', 'Form', 'Form1.Width',
IntToStr(Form1.Width)));
Form1.Height := StrToInt(INIRead('Otions.ini', 'Form', 'Form1.Height',
IntToStr(Form1.Height)));
Form1.Left := StrToInt(INIRead('Otions.ini', 'Form', 'Form1.Left',
IntToStr(Form1.Left)));
Form1.Top := StrToInt(INIRead('Otions.ini', 'Form', 'Form1.Top', IntToStr(Form1.Top)));
Panel1.Width := StrToInt(INIRead('Otions.ini', 'Panel1', 'Panel1.Width',
IntToStr(Panel1.Width)));
{Свойста проекта}
//Запускать ли игру Fals True
ValueListEditor1.Strings[0] := ('Запускать L2.exe=True');
// ValueListEditor1.ItemProps[0].EditStyle:= esPickList;
ValueListEditor1.ItemProps[0].PickList.Add('True');
ValueListEditor1.ItemProps[0].PickList.Add('False');
//Путь до игры
ValueListEditor1.ItemProps[1].EditStyle := esEllipsis;
ValueListEditor1.ItemProps[1].ReadOnly := True;
//Загрузить настройки проекта при старте
ValueListEditor1.Strings[1] := ('Путь к L2.exe=');
ValueListEditor1.Refresh;
ValueListEditor1.AutoSizeColumn(0);
//Дизайнер
Decorat := TDecorat.Create();
end;
//При загрузки формы
procedure TForm1.FormShow(Sender: TObject);
begin
LoadComponent();
hltSynedit1 := TSynFacilComplet.Create(self); //my highlighter
Synedit1.Highlighter := hltSynedit1; //optional if we are going to use SelectEditor()
hltSynedit1.LoadFromFile('Log.xml'); //load syntax
hltSynedit1.SelectEditor(Synedit1); //assign to editor
end;
//Открыть окно создания проекта
procedure TForm1.MenuItem10Click(Sender: TObject);
begin
Form4.ShowModal;
end;
//Откроем проект через диалог - Вызываем функцию Вывод каталога в TreeView1
procedure TForm1.MenuItem12Click(Sender: TObject);
var
g: integer;
begin
if OpenDialog1.Execute then
// if ExtractFileName(OpenDialog1.FileName) = 'Projects.luse' then
begin
TreeViewObject := TStringList.Create;
for i := 0 to TreeViewObject.Count - 1 do
begin
if TreeView1.Items.Item[i].Data <> nil then
Dispose(PString(TreeView1.Items.Item[i].Data));
end;
TreeView1.Items.Clear;
FilePathToTreeNode(TreeView1, nil, ExtractFilePath(OpenDialog1.FileName) +
'', True);
with TStringList.Create do
begin
LoadFromFile(OpenDialog1.FileName);
for g := 0 to Count - 1 do
ValueListEditor1.Cells[1, g + 1] := Strings[g];
end;
ProjectDir := ExtractFilePath(OpenDialog1.FileName);
ValueListEditor1.Enabled := True;
end;
end;
//Сохранить в файл Активный SynEdit
procedure TForm1.MenuItem13Click(Sender: TObject);
begin
if ExtendedNotebook1.ControlCount <> 0 then
begin
SynEditComadeTab('SaveFile', '');
ExtendedNotebook1.ActivePage.ImageIndex := 1349;
// ToolButton3.ImageIndex := 772;
end;
end;
//Закрыть вкладку
procedure TForm1.MenuItem16Click(Sender: TObject);
begin
SynEditComadeTab('CloseTab', '');
end;
//Удалить файл
procedure TForm1.MenuItem21Click(Sender: TObject);
begin
DeleteFile(string(TreeView1.Selected.Data));
TreeView1.Selected.Free;
end;
//Копировать
procedure TForm1.MenuItem22Click(Sender: TObject);
begin
SynEditComadeTab('Copy', '');
end;
//Вставить
procedure TForm1.MenuItem23Click(Sender: TObject);
begin
SynEditComadeTab('Paste', '');
end;
//Выделить все
procedure TForm1.MenuItem24Click(Sender: TObject);
begin
SynEditComadeTab('SelectAll', '');
end;
//Удалить выделеное
procedure TForm1.MenuItem25Click(Sender: TObject);
begin
SynEditComadeTab('ClearSelection', '');
end;
//Окно создания шаблона кода
procedure TForm1.MenuItem26Click(Sender: TObject);
begin
Form2.ShowModal;
end;
//Создать папку в TreeView1
procedure TForm1.MenuItem28Click(Sender: TObject);
var
NodeCaption: string; //для получения заголовка нового узла
NewNode: TTreeNode; //для создания нового узла
begin
//сначала очистим заголовок:
NodeCaption := '';
//теперь, если пользователь не ввел заголовок нового узла, выходим:
if not InputQuery('Ввод заголовка',
'Введите заголовок раздела', NodeCaption) then
exit;
//если мы здесь, то заголовок есть. создаем родительский узел:
NewNode := TreeView1.Items.Add(nil, NodeCaption);
//присваиваем ему картинку под индексом 0:
//папка
NewNode.ImageIndex := 1108;
NewNode.SelectedIndex := 1108;
end;
//Создать файл в TreeView1
procedure TForm1.MenuItem29Click(Sender: TObject);
var
NodeCaption: string;
NewNode: TTreeNode;
u: Pointer;
begin
PageControl1.ActivePageIndex := 0;
//Активировать вкладку редактор
if ExtractFileExt(string(TreeView1.Selected.Data)) = '' then
begin
NodeCaption := '';
if not InputQuery('Новый класс',
'Введите имя класса без расширения', NodeCaption) then
exit;
if NodeCaption > '' then
begin
NewNode := TreeView1.Items.AddChild(TreeView1.Selected, NodeCaption);
if NewNode.Parent = nil then
NewNode.ImageIndex := 0
//Файл
else
begin
if (NewTab(ExtractFileName(NodeCaption)) = True) then
begin
SynEditComadeTab('Hint', string(TreeView1.Selected.Data) +
'' + NodeCaption + '.uc');
SynEditComadeTab('SaveFile', '');
SynEditComadeTab('LoadFile', string(TreeView1.Selected.Data) +
'' + NodeCaption + '.uc');
NewNode.Data := nil;
string(u) := string(TreeView1.Selected.Data) + '' + NodeCaption + '.uc';
NewNode.Data := u;
NewNode.ImageIndex := 804;
NewNode.SelectedIndex := 785;
TreeView1.Selected.Expand(True);
NewNode.MakeVisible;
TreeView1.Selected := NewNode;
end;
end;
end;
end;
end;
//Редактирование компонента
procedure TForm1.MenuItem30Click(Sender: TObject);
begin
//ShowMessage((SenderToolButton as TToolButton).Caption);
if (ExtractFileExt((SenderToolButton as TToolButton).Caption) > '') and
(NewTab(ExtractFileName((SenderToolButton as TToolButton).Caption)) = True) then
begin
SynEditComadeTab('LoadFile', (SenderToolButton as TToolButton).Caption);
SynEditComadeTab('Hint', (SenderToolButton as TToolButton).Caption);
end;
end;
//Удалить компонент
procedure TForm1.MenuItem31Click(Sender: TObject);
begin
case QuestionDlg('Удалить компонент',
'Вы точно хотите удалить выбронный компонент?',
mtCustom, [mrYes, 'Да', mrNo, 'Нет', 'IsDefault'], '') of
mrYes:
begin
if ExtractFileExt((SenderToolButton as TToolButton).Caption) > '' then
begin
DeleteDirectory(ExtractFilePath(
(SenderToolButton as TToolButton).Caption), False);
(SenderToolButton as TToolButton).Free;
QuestionDlg('Удален',
'Компонент удален безвозвратно!',
mtCustom, [mrOk, 'Ок'], '');
end;
end;
end;
end;
//Кнопка собрать копиляция в меню
procedure TForm1.MenuItem33Click(Sender: TObject);
var
CompilT: CompilThread;
begin
//PageControl1.ActivePageIndex:=0;//Активировать вкладку редактор
if TreeView1.Items.Count <> 0 then
begin
if GroupBox1.Visible = False then
begin
GroupBox1.Visible := True;
MenuItem10.Checked := True;
end;
Form1.SynEdit1.Color := clWhite;
CompilT := CompilThread.Create(True);
CompilT.FreeOnTerminate := True;
CompilT.Priority := tpLower;
CompilT.Start;
MenuItem13.Click;
Form1.ToolButton5.Enabled := False;
Timer1.Enabled := True;
min := 0;
sec := 0;
GroupBox1.Height := 120;
//Удалить маркер с ошибкой
if (ActiveControl is TSynEdit) then
begin
for i := 0 to TSynEdit(ActiveControl).Lines.Count - 1 do
TSynEdit(ActiveControl).Marks.ClearLine(i);
TheLine := -1;
TSynEdit(ActiveControl).Invalidate;
end;
end
else
begin
SynEdit1.Lines[0] :=
'Для компиляции проекта создайте его или откройте!';
GroupBox1.Height := 120;
end;
end;
//Форматирование кода
procedure TForm1.MenuItem34Click(Sender: TObject);
var
i: integer;
s: string;
SRE: TStringList;
Te: TSynEdit;
begin
if Assigned(ExtendedNotebook1.ActivePage) then
begin
Te := SynEditComadeTab('SenderSynEdit', '');
SRE := TStringList.Create;
with TStringList.Create do
begin
Text := Te.Text;
for i := 0 to Count - 1 do
begin
s := trim(strings[i]);
//Удалить Где встречается //
// system.Delete(s, Pos('//', s), Length(s));
// system.Delete(s, Pos('debug', s), Length(s));
// удалить лишние пробелы и табы
while (s <> '') and (pos(' ', s) <> 0) do
s := StringReplace(s, ' ', ' ', [rfreplaceall]);
s := StringReplace(s, #9, ' ', [rfReplaceAll]);
s := #9 + s;
s := StringReplace(s, '}', #13'}'#13, [rfReplaceAll]);
s := StringReplace(s, '{', #13'{'#13, [rfReplaceAll]);
s := StringReplace(s, #9'function', #13'function', [rfReplaceAll]);
s := StringReplace(s, #9'local', ' local', [rfReplaceAll]);
s := StringReplace(s, #9'class', 'class', [rfReplaceAll]);
s := StringReplace(s, #9'var', ' var', [rfReplaceAll]);
if (Trim(strings[i]) = '') then
else
begin
SRE.add(s);
end;
end;
end;
Te.Clear;
Te.Text := SRE.Text;
SRE.Free;
ToolButton3.ImageIndex := 770;
ExtendedNotebook1.ActivePage.ImageIndex := 770;
end;
end;
//Копировать имя файлка вкладки в бувер
procedure TForm1.MenuItem35Click(Sender: TObject);
begin
Clipboard.AsText := ExtendedNotebook1.ActivePage.Caption;
end;
//Отмена Menu
procedure TForm1.MenuItem37Click(Sender: TObject);
begin
if ExtendedNotebook1.ControlCount <> 0 then
SynEditComadeTab('Undo', '');
end;
//Повтор Menu
procedure TForm1.MenuItem38Click(Sender: TObject);
begin
if ExtendedNotebook1.ControlCount <> 0 then
SynEditComadeTab('Redo', '');
end;
//Копировать путь к файлу вкладки
procedure TForm1.MenuItem40Click(Sender: TObject);
begin
Clipboard.AsText := ExtendedNotebook1.ActivePage.Hint;
end;
//Закоментировать строку
procedure TForm1.MenuItem43Click(Sender: TObject);
begin
if (ActiveControl is TSynEdit) then
begin
TSynEdit(ActiveControl).LineText := '//' + TSynEdit(ActiveControl).LineText;
end;
end;
//Оптимизировать все
procedure TForm1.MenuItem50Click(Sender: TObject);
begin
if Assigned(ExtendedNotebook1.ActivePage) then
Optimization.Optimize.Full(Form1.SynEditComadeTab('SenderSynEdit', ''));
end;
//Открыть XdatEditor
procedure TForm1.MenuItem52Click(Sender: TObject);
var
P: TProcessUTF8;
begin
P := TProcessUTF8.Create(nil);
P.ShowWindow := swoShow;
P.Executable := 'ProgramXdatEditorXdatEditor.exe';
P.Execute;
end;
//Вырезать
procedure TForm1.MenuItem53Click(Sender: TObject);
begin
if ExtendedNotebook1.ControlCount <> 0 then
SynEditComadeTab('Cut', '');
end;
//Закрыть все вкладки кроме этой
procedure TForm1.MenuItem54Click(Sender: TObject);
begin
SynEditComadeTab('CloseFullTab', '');
end;
//Поиск MenuItem
procedure TForm1.MenuItem55Click(Sender: TObject);
begin
MenuItem6.Click;
end;
//Открыть форму поиска
procedure TForm1.MenuItem6Click(Sender: TObject);
begin
Form3.Show;
if (ActiveControl is TSynEdit) then
begin
Form3.ComboBox1.Text := TSynEdit(ActiveControl).SelText;
end;
end;
//Показать скрыть окно проекта
procedure TForm1.MenuItem7Click(Sender: TObject);
begin
if Panel1.Visible = False then
begin
Panel1.Visible := True;
end
else
begin
Panel1.Visible := False;
end;
end;
//Показать скрыть окно сообщения
procedure TForm1.MenuItem8Click(Sender: TObject);
begin
if GroupBox1.Height = 0 then
begin
GroupBox1.Height := 120;
end
else
begin
GroupBox1.Height := 0;
end;
end;
//открыть файл с ошибкой + поиск текста в файла (Log)
procedure TForm1.SynEdit1DblClick(Sender: TObject);
begin
i := 0;
PageControl1.ActivePageIndex := 0;
//Активировать вкладку редактор
if (Trim(ExtractWord(1, SynEdit1.LineText, ['|'])) = 'ПРОСМОТР') then
begin
if ExtendedNotebook1.PageCount > 0 then
for i := 0 to ExtendedNotebook1.PageCount - 1 do
if Trim(ExtractWord(2, SynEdit1.LineText, ['|', '('])) =
Trim(ExtendedNotebook1.Page[i].Hint) then
begin
ExtendedNotebook1.Pages[i].TabVisible := True;
ExtendedNotebook1.ActivePageIndex := i;
SynEditComadeTab('LineIndex',
Trim(ExtractWord(2, SynEdit1.LineText, ['(', ')'])));
exit;
end;
if (NewTab(ExtractFileName(Trim(ExtractWord(2, SynEdit1.LineText, ['|', '('])))) =
True) then
begin
SynEditComadeTab('Hint', Trim(ExtractWord(2, SynEdit1.LineText, ['|', '('])));
SynEditComadeTab('LoadFile', Trim(ExtractWord(2, SynEdit1.LineText, ['|', '('])));
SynEditComadeTab('LineIndex', Trim(ExtractWord(2, SynEdit1.LineText, ['(', ')'])));
end;
end;
//Поиск текста в файле результат открыть файл по клику (Log)
if (Trim(ExtractWord(1, SynEdit1.LineText, ['|'])) = 'Файл') then
begin
if ExtendedNotebook1.PageCount > 0 then
for i := 0 to ExtendedNotebook1.PageCount - 1 do
if Trim(ExtractWord(2, SynEdit1.LineText, ['|', '*'])) =
Trim(ExtendedNotebook1.Page[i].Hint) then
begin
ExtendedNotebook1.Pages[i].TabVisible := True;
ExtendedNotebook1.ActivePageIndex := i;
//SynEditComadeTab('LineIndex',
// Trim(ExtractWord(2, SynEdit1.LineText, ['(', ')'])));
exit;
end;
if (NewTab(ExtractFileName(Trim(ExtractWord(2, SynEdit1.LineText, ['|', '*'])))) =
True) then
begin
SynEditComadeTab('Hint', Trim(ExtractWord(2, SynEdit1.LineText, ['|', '*'])));
SynEditComadeTab('LoadFile', Trim(ExtractWord(2, SynEdit1.LineText, ['|', '*'])));
//SynEditComadeTab('LineIndex', Trim(ExtractWord(2, SynEdit1.LineText, [' ', ':'])));
end;
end;
end;
// Расцветка лога
procedure TForm1.SynEdit1SpecialLineColors(Sender: TObject; Line: integer;
var Special: boolean; var FG, BG: TColor);
begin
if ((line mod 1) = 0) and (SynEdit1.Lines[0] = 'Готово!') then
begin
Special := True;
BG := $0072F968;
end;
if ((line mod 1) = 0) and (SynEdit1.Lines[0] =
'Компиляция проекта: ...') then
begin
Special := True;
BG := clYellow;
end;
if ((line mod 1) = 0) and (SynEdit1.Lines[0] = 'Запуск игры...') then
begin
Special := True;
BG := clAqua;
end;
if ((line mod 1) = 0) and (SynEdit1.Lines[0] =
'Файл скомпилирован в папку проекта!') then
begin
Special := True;
BG := $0072F968;
end;
if ((line mod 1) = 0) and (SynEdit1.Lines[0] = 'Готово!') then
begin
Special := True;
BG := $0072F968;
end;
if ((line mod 1) = 0) and (SynEdit1.Lines[0] =
'Для компиляции проекта создайте его или откройте!') then
begin
Special := True;
BG := clYellow;
end;
if ((line mod 1) = 0) and (SynEdit1.Lines[0] =
'Ошибка: Неудалось скомпилировать файл..') then
begin
Special := True;
BG := clred;
FG := clWhite;
end;
end;
//Таймер копиляции
procedure TForm1.Timer1Timer(Sender: TObject);
begin
sec := sec + 1;
if sec = 60 then
begin
min := min + 1;
sec := 0;
end;
if sec < 10 then
secs := '0' + IntToStr(sec);
if sec > 9 then
secs := IntToStr(sec);
if min < 10 then
mins := '0' + IntToStr(min);
if min > 9 then
mins := IntToStr(min);
end;
//Обновим выброный элемент анимация визуальный редатор
procedure TForm1.Timer2Timer(Sender: TObject);
begin
Decorat.ImgUpdate();
end;
//Создать кнопкку в визуальном редакторе
procedure TForm1.ToolButton10Click(Sender: TObject);
begin
Decorat.DecoratorCreateComponent('Button');
TreeView2.Items.AddChild(TreeView2.Items.Item[0],
'Button' + IntToStr(TreeView2.Items.Item[0].Count)).ImageIndex := 2394;
end;
//Сохранить настроки в INI приложения при закрытие программы
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: boolean);
begin
// hltSynedit1.UnSelectEditor; //release editor (only necessary if we are to call to SelectEditor(), again)
//hltSynedit1.Free; //destroy the highlighter
INIWrite('Otions.ini', 'Form', 'Form1.Width', IntToStr(Form1.Width));
INIWrite('Otions.ini', 'Form', 'Form1.Height', IntToStr(Form1.Height));
INIWrite('Otions.ini', 'Form', 'Form1.Left', IntToStr(Form1.Left));
INIWrite('Otions.ini', 'Form', 'Form1.Top', IntToStr(Form1.Top));
INIWrite('Otions.ini', 'Panel1', 'Panel1.Width', IntToStr(Panel1.Width));
end;
//Обновить TObject для автозапонения
procedure TForm1.ExtendedNotebook1Change(Sender: TObject);
begin
if (ActiveControl is TSynEdit) then
begin
hlt.SelectEditor(TSynEdit(ActiveControl)); //assign to editor
end;
end;
//Смысл в том, что хотим выделить таб, при нажатии на нем правой клавишей мыши (вызов меню)
procedure TForm1.ExtendedNotebook1MouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: integer);
var
SelectPoint: TPoint;
begin
SelectPoint.x := X;
SelectPoint.y := Y;
ExtendedNotebook1.ActivePageIndex := ExtendedNotebook1.IndexOfPageAt(SelectPoint);
end;
//При клике на элемент в TreeView1 загрузить файл в таб
procedure TForm1.TreeView1DblClick(Sender: TObject);
var
i: integer;
begin
PageControl1.ActivePageIndex := 0;
//Активировать вкладку редактор
if ExtendedNotebook1.PageCount > 0 then
for i := 0 to ExtendedNotebook1.PageCount - 1 do
if string(TreeView1.Selected.Data) = ExtendedNotebook1.Page[i].Hint then
begin
ExtendedNotebook1.Pages[i].TabVisible := True;
ExtendedNotebook1.ActivePageIndex := i;
exit;
end;
try
if (ExtractFileExt(trim(string(TreeView1.Selected.Data))) > '') then
if (NewTab(ExtractFileName(string(TreeView1.Selected.Data))) = True) then
begin
SynEditComadeTab('Hint', string(TreeView1.Selected.Data));
SynEditComadeTab('LoadFile', string(TreeView1.Selected.Data));
end;
finally
end;
// ShowMessage(string(TreeView1.Selected.Data));
end;
//Указать путь к игре в редакторе проета
procedure TForm1.ValueListEditor1ButtonClick(Sender: TObject; aCol, aRow: integer);
begin
if aCol = 1 then
if OpenDialog2.Execute then
if ExtractFileName(OpenDialog2.FileName) = 'L2.exe' then
ValueListEditor1.Strings[1] := ('Путь к L2.exe=' + OpenDialog2.FileName);
end;
//Сохранить настройки проекта
procedure TForm1.ValueListEditor1EditingDone(Sender: TObject);
var
info: TStringList;
begin
info := TStringList.Create;
info.Add(Form1.ValueListEditor1.Cells[1, 1]);
info.Add(Form1.ValueListEditor1.Cells[1, 2]);
info.SaveToFile(ProjectDir + 'Projects.le');
Application.ProcessMessages;
end;
//Настройки визуального редактора компонента
procedure TForm1.ValueListEditor2KeyUp(Sender: TObject; var Key: word;
Shift: TShiftState);
begin
if Decorat.ButtonActive <> nil then
begin
Decorat.ButtonActive.Caption := ValueListEditor2.Cells[1, 2];
Decorat.ImageUpdate.Width := StrToInt(Form1.ValueListEditor2.Cells[1, 4]);
Decorat.ImageUpdate.Height := StrToInt(Form1.ValueListEditor2.Cells[1, 5]);
Decorat.ImageUpdate.Top := StrToInt(Form1.ValueListEditor2.Cells[1, 6]);
Decorat.ImageUpdate.Left := StrToInt(Form1.ValueListEditor2.Cells[1, 7]);
end;
end;
//Вывод каталога в TreeView1 Пример:
//FilePathToTreeNode(TreeView1, nil, 'Папка' + '', True);
procedure TForm1.FilePathToTreeNode(aTreeView: TTreeView; aRoot: TTreeNode;
Path: string; Recurse: boolean);
var
NewNode: TTreeNode;
SRec: TSearchRec;
begin
if FindFirst(Path + '*.*', SysUtils.faAnyFile, SRec) = 0 then
repeat
if (sRec.Name = '.') or (sRec.Name = '..') then
Continue;
begin
TreeViewObject.Add(PChar(Path + srec.Name));
NewNode := aTreeView.Items.AddChildObject(aRoot, SRec.Name,
PChar(TreeViewObject.Strings[i]));
Inc(i);
end;
if Recurse and ((srec.Attr and SysUtils.faDirectory) <> 0) then
begin
FilePathToTreeNode(aTreeView, NewNode, Path + srec.Name + '', True);
//папка
NewNode.ImageIndex := 1108;
NewNode.SelectedIndex := 1108;
end
else
begin
//Файл
NewNode.ImageIndex := 804;
NewNode.SelectedIndex := 785;
end;
until FindNext(SRec) <> 0;
FindClose(SRec);
end;
//Запись в INI Пример:
//INIWrite('Otions.ini','Section', 'Key','тест');
procedure TForm1.INIWrite(FileName: string; Section: string; Key: string;
Param: string);
begin
if (FileExists(FileName)) then
begin
Inif := TINIFile.Create(FileName);
INiF.WriteString(Section, Key, Param);
end;
end;
//Чтение из INI Пример:
//INIRead('Otions.ini','Section', 'Key','тест');
function TForm1.INIRead(FileName: string; Section: string; Key: string;
Param: string): string;
begin
if (FileExists(FileName)) then
begin
Inif := TINIFile.Create(FileName);
Result := INiF.ReadString(Section, Key, Param);
end;
end;
//Создание вкладки
function TForm1.NewTab(TabCaption: string): boolean;
begin
Tab := TTabSheet.Create(self);
Tab.PageControl := ExtendedNotebook1;
Tab.Caption := TabCaption;
Tab.PageControl.ActivePage := Tab;
Tab.ImageIndex := 1349;
Tab.Visible := False;
Tab.OnShow := @rTabShow;
rSynEdit := TSynEdit.Create(Tab);
rSynEdit.Parent := Tab;
rSynEdit.Align := alClient;
rSynEdit.OnUTF8KeyPress := @rUTF8KeyPress;
rSynEdit.OnChange := @rChange;
rSynEdit.OnMouseMove := @rMouseMove;
rSynEdit.OnKeyUp := @rKeyUp;
rSynEdit.OnKeyPress := @rKeyPress;
rSynEdit.OnClick := @rOnClick;
rSynEdit.OnSpecialLineMarkup := @GetLineColor;
rSynEdit.RightEdge := 0;
rSynEdit.BorderStyle := bsNone;
rSynEdit.Color := clWhite;
rSynEdit.Font := SynEdit1.Font;
rSynEdit.MouseOptions := SynEdit1.MouseOptions;
rSynEdit.Options := SynEdit1.Options;
rSynEdit.Options2 := SynEdit1.Options2;
rSynEdit.Text := 'class ' + TabCaption + ' extends UICommonAPI;' +
#13#13#13 + 'defaultproperties{}';
rSynEdit.Name := 'syed' + IntToStr(ExtendedNotebook1.PageCount);
rSynEdit.PopupMenu := PopupMenu3;
hlt := TSynFacilComplet.Create(self); //my highlighter
rSynEdit.Highlighter := hlt; //optional if we are going to use SelectEditor()
hlt.LoadFromFile('UnrealScript.xml'); //load syntax
hlt.SelectEditor(rSynEdit); //assign to editor
hlt.IconList := ImageList1;
Result := True;
Tab.Visible := True;
end;
//Команды выполнения для компонента TSynEdit и PageControl1
function TForm1.SynEditComadeTab(Comade, Param: string): TSynEdit;
var
syed: TSynEdit;
x1, x2, i, pageindex: integer;
begin
//Проверка существования Page TSynEdit
if Assigned(ExtendedNotebook1.ActivePage) then
begin
syed := TSynEdit(ExtendedNotebook1.ActivePage.Components[0]);
// Если все ОК выполняем команды
case Comade of
//Тут хранится путь к файлу
'Hint': ExtendedNotebook1.ActivePage.Hint := Param;
//Загрузим текст
'Text': syed.Text := Param;
//Загрузим текст из файла
'LoadFile': syed.Lines.LoadFromFile(UTF8ToSys(Param));
//Сохраним текст в файл
'SaveFile': syed.Lines.SaveToFile(UTF8ToSys(ExtendedNotebook1.ActivePage.Hint));
//Вставить текст в позицию курсора
'SelText': syed.SelText := Param;
//Перейти на Index строки
'LineIndex':
begin
syed.TopLine := StrToInt(Param) - 10;
//x1 := PosEx(Trim(syed.Lines[StrToInt(Param)]), Trim(syed.Text));
//x2 := PosEx(Trim(syed.Lines[StrToInt(Param)]), Trim(syed.Text)) +length(Trim(syed.Lines[StrToInt(Param)]));
//syed.SelStart := x1;
//syed.SelEnd := x2;
syed.CaretY := StrToInt(Param);
// syed.SetFocus;
m := TSynEditMark.Create(syed);
m.Line := StrToInt(Param);
m.ImageList := ImageList1;
m.ImageIndex := 1526;
m.Visible := True;
syed.Marks.Add(m);
TheLine := StrToInt(Param);
syed.Invalidate;
end;
//Копировать
'Copy': syed.CopyToClipboard;
//Вставить
'Paste': syed.PasteFromClipboard;
//Вырезать
'Cut': syed.CutToClipboard;
//Выделить все
'SelectAll': syed.SelectAll;
//Удалить выделеное
'ClearSelection': syed.ClearSelection;
//Отмена
'Undo': syed.Undo;
//Вернуть
'Redo': syed.Redo;
//Отправим Sender SynEdit
'SenderSynEdit': Result := syed;
//Закрыть активную вкладку
'CloseTab':
begin
ExtendedNotebook1.ActivePage.Free;
end;
//Закрыть все табы кроме ативного
'CloseFullTab':
begin
pageindex := ExtendedNotebook1.ActivePageIndex;
for i := ExtendedNotebook1.PageCount - 1 downto 0 do
if pageindex <> i then
ExtendedNotebook1.Page[i].Free;
end;
else
ShowMessage(Comade + 'Comade Не известнена!');
end;
end;
end;
//SynFacilComplet
procedure TForm1.rKeyUp(Sender: TObject; var Key: word; Shift: TShiftState);
begin
hlt.KeyUp(Sender, Key, Shift);
end;
//SynFacilComplet
procedure TForm1.rUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
begin
if (ActiveControl is TSynEdit) then
begin
hlt.SelectEditor(TSynEdit(ActiveControl)); //assign to editor
hlt.UTF8KeyPress(Sender, UTF8Key);
end;
end;
//Загрузить компоненты
procedure TForm1.LoadComponent();
var
Pic: TPortableNetworkGraphic;
i: integer;
PascalFiles, FilePathComponent: TStringList;
begin
ImageList2.Clear;
PascalFiles := TStringList.Create;
FilePathComponent := TStringList.Create;
try
FindAllFiles(PascalFiles, 'components', '*.lc', True);
// ... тут читаем файл в StringList:
if PascalFiles.Text > '' then
Form1.ToolBar2.ButtonList.Clear;
for i := 0 to PascalFiles.Count - 1 do
begin
ToolBar2.ButtonList.Add(TToolButton.Create(Self));
FilePathComponent.LoadFromFile(PascalFiles.Strings[i]);
Pic := TPortableNetworkGraphic.Create;
if FileExists(ExtractFilePath(PascalFiles.Strings[i]) + 'icon.png') = True then
begin
Pic.LoadFromFile(ExtractFilePath(PascalFiles.Strings[i]) + 'icon.png');
ImageList2.Add(Pic, nil);
ToolBar2.Buttons[ToolBar2.ButtonCount - 1].Hint := FilePathComponent.Strings[0];
ToolBar2.Buttons[ToolBar2.ButtonCount - 1].Caption := PascalFiles.Strings[i];
ToolBar2.Buttons[ToolBar2.ButtonCount - 1].Parent := ToolBar2;
ToolBar2.Images := ImageList2;
ToolBar2.Buttons[ToolBar2.ButtonCount - 1].ImageIndex := i;
ToolBar2.Buttons[ToolBar2.ButtonCount - 1].PopupMenu := ComponentMenu;
ToolBar2.Buttons[ToolBar2.ButtonCount - 1].OnMouseDown := @rToolMouseDown;
ToolBar2.Buttons[ToolBar2.ButtonCount - 1].OnClick := @rToolOnClick;
end;
Pic.Free;
end;
finally
PascalFiles.Free;
FilePathComponent.Free;
end;
end;
//Получаем Sender и заносим в переменную SenderToolButton
procedure TForm1.rToolMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: integer);
begin
SenderToolButton := Sender;
end;
//Обработка нажатия на компонент
procedure TForm1.rToolOnClick(Sender: TObject);
var
FilePathComponent: TStringList;
begin
if ExtendedNotebook1.ControlCount <> 0 then
begin
FilePathComponent := TStringList.Create;
FilePathComponent.LoadFromFile((Sender as TToolButton).Caption);
FilePathComponent.Strings[0] := '';
SynEditComadeTab('SelText', FilePathComponent.Text);
FilePathComponent.Free;
end;
end;
//Обновить иконку не сохранено
procedure TForm1.rChange(ASender: TObject);
begin
ToolButton3.ImageIndex := 770;
ExtendedNotebook1.ActivePage.ImageIndex := 770;
end;
//Обновляем ссылку на SynPluginSyncroEdit1
procedure TForm1.rMouseMove(Sender: TObject; Shift: TShiftState; X, Y: integer);
begin
//(Sender as TWinControl).SetFocus;
if (ActiveControl is TSynEdit) then
begin
SynPluginSyncroEdit1.Editor := TSynEdit(ActiveControl);
end;
end;
//При загрузке Tab
procedure TForm1.rTabShow(Sender: TObject);
var
MyObj: TComponent;
SynMarkup: TSynEditMarkupHighlightAllCaret;
begin
MyObj := ExtendedNotebook1.ActivePage.Components[0];
TSynEdit(MyObj).SetFocus;
SynMarkup := TSynEditMarkupHighlightAllCaret(
TSynEdit(MyObj).MarkupByClass[TSynEditMarkupHighlightAllCaret]);
SynMarkup.MarkupInfo.FrameColor := clWindowFrame;
SynMarkup.MarkupInfo.Background := $0080FF80;
SynMarkup.WaitTime := 100; // millisec
SynMarkup.Trim := True; // no spaces, if using selection
SynMarkup.FullWord := True;
// only full words If "Foo" is under caret, do not mark it in "FooBar"
SynMarkup.IgnoreKeywords := False;
//Снять выделение ошибки
TheLine := -1;
end;
procedure TForm1.rKeyPress(Sender: TObject; var Key: char);
begin
// if (key=char(17))and(key=char(191)) then
end;
//Всплывающая Подсказкак функции
procedure TForm1.rOnClick(Sender: TObject);
begin
if (ActiveControl is TSynEdit) then
begin
//Снять выделение ошибки
TheLine := -1;
TSynEdit(ActiveControl).Invalidate;
//Всплывающая Подсказкак функции
if (Trim(ExtractWord(0, TSynEdit(ActiveControl).LineText, [' ', '('])) =
'OnRegisterEvent') then
begin
// PopupNotifier1.Text:= 'Функции — это блок кода, который вы можете использовать в любом участке вашей программы неограниченное количество раз. Например, в программе ниже мы выводим 2 строки (без применения функций):';
// PopupNotifier1.ShowAtPos(mouse.CursorPos.x,mouse.CursorPos.y);
end;
//PopupNotifier1.Visible:=false;
end;
end;
// Подсветка строки с ощибкой
procedure TForm1.GetLineColor(Sender: TObject; Line: integer;
var Special: boolean; Markup: TSynSelectedColor);
begin
if Line <> TheLine then
exit;
Special := True;
Markup.Background := clRed;
// Выделить строку с номером TheLine красным цветом
Markup.Style := [fsBold];
// Выделить строку с номером TheLine красным цветом
end;
//вывести ошибку после компиляции
procedure TForm1.SynEditERROR();
var
f, i: integer;
begin
for f := SynEdit1.Lines.Count - 1 downto 0 do
begin
if pos('ПРОСМОТР|', SynEdit1.Lines[f]) > 0 then
begin
if ExtendedNotebook1.PageCount > 0 then
for i := 0 to ExtendedNotebook1.PageCount - 1 do
if Trim(ExtractWord(2, SynEdit1.Lines[f], ['|', '('])) =
Trim(ExtendedNotebook1.Page[i].Hint) then
begin
ExtendedNotebook1.Pages[i].TabVisible := True;
ExtendedNotebook1.ActivePageIndex := i;
SynEditComadeTab('LineIndex',
Trim(ExtractWord(2, SynEdit1.Lines[f], ['(', ')'])));
exit;
end;
if (NewTab(ExtractFileName(Trim(ExtractWord(2, SynEdit1.Lines[f], ['|', '('])))) =
True) then
begin
SynEditComadeTab('Hint', Trim(ExtractWord(2, SynEdit1.Lines[f], ['|', '('])));
SynEditComadeTab('LoadFile',
Trim(ExtractWord(2, SynEdit1.Lines[f], ['|', '('])));
SynEditComadeTab('LineIndex',
Trim(ExtractWord(2, SynEdit1.Lines[f], ['(', ')'])));
// ShowMessage(Trim(ExtractWord(2, SynEdit1.Lines[f], ['|', '('])));
exit;
end;
end;
end;
end;
end.
Добавлено спустя 21 секунду:
LearnMagic писал(а):https://www.bsuir.by/m/12_100229_1_98218.pdf
http://www.gunsmoker.ru/2011/01/blog-post.html
Спасибо читаю!
Добавлено спустя 43 минуты 33 секунды:
Еще вопросом задался а можно ли обработчики визуальных компонентов главной формы перенести в отдельный unit?
Вот например в JavaScript Можно перехватить обработчик в pascal интересно можно это сделать?)
Я к тому что бы например все обработчики форм перенести в 1 unit
Указатели на функции думаю грамотка получится(
Может как то в классе можно указать procedure TForm1.Button1Click(Sender: TObject); overload; В другом Unit_555
-
LearnMagic
- новенький
- Сообщения: 66
- Зарегистрирован: 10.11.2016 22:13:38
BIT писал(а):Еще вопросом задался а можно ли обработчики визуальных компонентов главной формы перенести в отдельный unit?
Можно, но не нужно и немного проблематично. Обработчик не посто procedure, а procedure of object. При вызове неявно передаётся Self.
При наследовании одной формы от другой, все обработчики из секции published позволяют использовать перекрытие в потомках без дополнительных описателей. Inherited ни кто не отменял.
По поводу оформления проектов см. главу 4 Стив Тейксейра, Ксавье Пачеко Delphi 5 Руководство разработчика
Достаточно понятен код или стоит изменить свой стиль написания кода?
Гораздо правильнее сразу давать кнопкам нормальные названия, а не писать такие комментарии. Для этого лучше подучить английский язык.
BIT писал(а):Вот например я так пишу всегда, Это пример главного Unit.
Достаточно понятен код или стоит изменить свой стиль написания кода?
Читаем и понятен, но скажу на своем опыте. Не так давно Zub ткнул меня носом в ActionList, я посмотрел, и проникся...
Код: Выделить всё
//Поиск ToolButton4
procedure TForm1.ToolButton4Click(Sender: TObject);
begin
MenuItem6.Click;
end;
такие конструкции просто теряют смысл, что снижает кол-во "буков написания" и захламление кода, соответственно...
Папки. Каждый делает по своему. - общего не видел
Сейчас я бы сделал так.
dist // Папка с готовым дистрибутивом (программный продукт) содержащий установочный файл или образ диска.
build // папки сборки, хлам всякий DCU, *.a
doc // Документация
src // Папка с исходным кодом программ
tools // Исполняемые файлы утилит
data // Данные
3rdparty // Исходный код сторонних бибилотек и утилит. А так же ПО и данные от контр агентов.
it // Айти средства: скрипт сборки дистрибутива, скрипты бекапа, развёртывания среды разработчика, сервера сборки, VNC клиенты.
3rdparty\dist
3rdparty\libs
3rdparty\apps
3rdparty\data
tools - Портативные утилиты
для примера клиент северное приложение.
data\client
data\server
data\common
data\tests
data\gen_tests
src\client
src\server
src\common - общий исходный код
src\tests
src\gen_tests
Добавлено спустя 31 минуту 46 секунд:
В интернете этого полно. https://en.wikipedia.org/wiki/Coding_conventions
Книгу качественный код и коде стайл тут уже привели.
Ещё хочу добавить стандарт https://en.wikipedia.org/wiki/MISRA_C
Он хотя и для Си, но некоторые вещи можно позаимствовать.
По поводу классов. Их надо делать максимально не завистными этому посещено много книг.
Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж.-Приемы объектно-ориентированного проектирования (2001)
Фаулер Мартин (Fowler Martiп)-Рефакторинг - улучшение существующего кода-Символ-Плюс (2003)
Ошероув Рой.-Искусство автономного тестирования с примерами на С#(2014)
Но что хочу добавить. Лучше конечно учится на готовых проектах.
По дизайну мне понравилось руководство от гугла:
https://developer.android.com/guide/pra ... index.html
А так же гноме
https://developer.gnome.org/hig/stable/
Сейчас я бы сделал так.
dist // Папка с готовым дистрибутивом (программный продукт) содержащий установочный файл или образ диска.
build // папки сборки, хлам всякий DCU, *.a
doc // Документация
src // Папка с исходным кодом программ
tools // Исполняемые файлы утилит
data // Данные
3rdparty // Исходный код сторонних бибилотек и утилит. А так же ПО и данные от контр агентов.
it // Айти средства: скрипт сборки дистрибутива, скрипты бекапа, развёртывания среды разработчика, сервера сборки, VNC клиенты.
3rdparty\dist
3rdparty\libs
3rdparty\apps
3rdparty\data
tools - Портативные утилиты
для примера клиент северное приложение.
data\client
data\server
data\common
data\tests
data\gen_tests
src\client
src\server
src\common - общий исходный код
src\tests
src\gen_tests
Добавлено спустя 31 минуту 46 секунд:
BIT писал(а):unit, class, функции, структуры и т.п
В интернете этого полно. https://en.wikipedia.org/wiki/Coding_conventions
Книгу качественный код и коде стайл тут уже привели.
Ещё хочу добавить стандарт https://en.wikipedia.org/wiki/MISRA_C
Он хотя и для Си, но некоторые вещи можно позаимствовать.
По поводу классов. Их надо делать максимально не завистными этому посещено много книг.
Гамма Э., Хелм Р., Джонсон Р., Влиссидес Дж.-Приемы объектно-ориентированного проектирования (2001)
Фаулер Мартин (Fowler Martiп)-Рефакторинг - улучшение существующего кода-Символ-Плюс (2003)
Ошероув Рой.-Искусство автономного тестирования с примерами на С#(2014)
Но что хочу добавить. Лучше конечно учится на готовых проектах.
По дизайну мне понравилось руководство от гугла:
https://developer.android.com/guide/pra ... index.html
А так же гноме
https://developer.gnome.org/hig/stable/
Последний раз редактировалось Pavia 21.01.2018 11:16:54, всего редактировалось 1 раз.
-
Mirage
- энтузиаст
- Сообщения: 881
- Зарегистрирован: 06.05.2005 20:29:07
- Откуда: Russia
- Контактная информация:
По юнитам и классам, советую погуглить SOLID и DRY. Если 15 лет опыта, то понимание уже должно быть, оформится во что-то конкретное.
По директориям не так важно какие именно, важно чтобы не было все вперемешку - исходники, бинарники, временный мусор и т.д.
Ну и, конечно, чтобы все это было в гите. Не в свне, а именно гите, ибо 2018 год на дворе.
По директориям не так важно какие именно, важно чтобы не было все вперемешку - исходники, бинарники, временный мусор и т.д.
Ну и, конечно, чтобы все это было в гите. Не в свне, а именно гите, ибо 2018 год на дворе.
Теперь еще на 15 лет изучения все, что вы мне посоветовали спасибо! 
- serbod
- постоялец
- Сообщения: 449
- Зарегистрирован: 16.09.2016 10:03:02
- Откуда: Минск
- Контактная информация:
Есть еще всякие алгоритмы, шаблоны проектирования, правила безопасности и прочий computer science. В обычных условиях не нужны, и голову ими забивать не надо. Согласно золотому правилу о преждевременной оптимизации. Но на масштабных проектах и при глубокой оптимизации столкнуться с этим все-таки придется.
Правила безопасности все же процитирую, там совсем немного, но польза огромная.
https://en.wikipedia.org/wiki/The_Power ... tical_Code
Правила безопасности все же процитирую, там совсем немного, но польза огромная.
https://en.wikipedia.org/wiki/The_Power ... tical_Code
Лично я выношу все алгоритмы, которые относятся именно к обработке данных в отдельный модуль и складываю их в папку Model (модель), отделяя его от представления (View), которые суть файлы форм и лежат в "корне" проекта.
От идеи создать большую библиотеку постоянно реиспользуемых функций я отказался - неудобно переносить на другую машину, потому у меня есть множество наработанных больше чем за десять лет алгоритмами, которые я просто копирую в специальный модуль в каждом новом проекте - common.pas. Это позволяет просто скопировать папку с проектом на другой компьютер и продолжиться работу (обычно для копирования применяю FreeFileSync, чтобы можно было без труда синхронизировать код, конечно, у GIT/Mercurial больше функций и для больших проектов на несколько человек альтернативы, собственно, нет, но для проекта, который ведёшь один, просто в нескольких местах, FreeFileSync вполне достаточен). Также этот метод решает проблему с тем, что временами приходится перелопачивать эти самые общие модули и они начинают работать немного не так, как ранее (например, добавлен новый функционал, добавлены новые аргументы для функций, но при этом параметры по умолчанию придумать не получается, или переименованы функции, чтобы лучше отражать назначение). Тем не менее, при устранении ошибок в общей библиотеке, приходится вспоминать, а не была ли такая же ошибка в целой куче других проектов? Так что у этого решения есть как плюсы, так и минусы (но для меня плюсов больше).
От идеи создать большую библиотеку постоянно реиспользуемых функций я отказался - неудобно переносить на другую машину, потому у меня есть множество наработанных больше чем за десять лет алгоритмами, которые я просто копирую в специальный модуль в каждом новом проекте - common.pas. Это позволяет просто скопировать папку с проектом на другой компьютер и продолжиться работу (обычно для копирования применяю FreeFileSync, чтобы можно было без труда синхронизировать код, конечно, у GIT/Mercurial больше функций и для больших проектов на несколько человек альтернативы, собственно, нет, но для проекта, который ведёшь один, просто в нескольких местах, FreeFileSync вполне достаточен). Также этот метод решает проблему с тем, что временами приходится перелопачивать эти самые общие модули и они начинают работать немного не так, как ранее (например, добавлен новый функционал, добавлены новые аргументы для функций, но при этом параметры по умолчанию придумать не получается, или переименованы функции, чтобы лучше отражать назначение). Тем не менее, при устранении ошибок в общей библиотеке, приходится вспоминать, а не была ли такая же ошибка в целой куче других проектов? Так что у этого решения есть как плюсы, так и минусы (но для меня плюсов больше).
