Delphi Graphics32 объединяет нормальные слои с слоями для рисования
Под рисованием слоя я подразумеваю слой, где пользователь может вручную рисовать линии, круги или другие фигуры. Под обычными слоями я подразумеваю слои, описанные в примере слоев graphics32 (слои, которые можно перемещать или изменять их размер во время выполнения с помощью событий мыши). Поэтому у меня возникают трудности при объединении этих двух типов слоев. В моем тестовом проекте на данный момент я предполагаю, что у меня есть только один слой чертежа и несколько слоев PNG. Поэтому в моем проекте я установил свойства для ImgView32 в OnFormCreate, например:
procedure TForm1.FormCreate(Sender: TObject);
begin
AWidth:= 800;
AHeight:= 600;
FillColor:=clWhite;
with ImgView do
begin
Selection := nil;
RBLayer := nil;
Layers.Clear;
Scale := 1;
Scaled:=true;
Bitmap.SetSize(AWidth, AHeight);
Bitmap.DrawMode := dmTransparent;
Bitmap.Clear(FillColor);
end;
end;
После этого, по нажатию кнопки, я добавляю несколько слоев (содержащих прозрачные изображения PNG). Так вот так
procedure TForm1.Button1Click(Sender: TObject);
begin
AddPNGLayer(1);
AddPNGLayer(2);
AddDrawingLayer;
AddPNGLayer(3);
end;
(Я не буду здесь подробно описывать добавление слоев PNG для того, чтобы вопрос был коротким. Скажу только, что в нем используется событие onMouseDown (layerMouseDown), отличное от того, которое используется в drawingLayer), а AddDrawingLayer выглядит следующим образом:
procedure TForm1.AddDrawingLayer;
var
P:TPoint;
jumaH, JumaW, W, H: Single;
begin
imwidth := ImgView.Bitmap.Width;
imheight := ImgView.Bitmap.Height;
xofx := (ImgView.ClientWidth - 17 - imwidth) div 2; // substracting the width of the scrollbar
yofy := (ImgView.ClientHeight - 17 - imheight) div 2; // same here with height
bm32 := TBitmap32.Create;
bm32.DrawMode := dmTransparent;
bm32.SetSize(ImgView.Bitmap.Width,ImgView.Bitmap.Height);
bm32.Canvas.Pen.Width := 3;
bm32.Canvas.Pen.Color := clBlack32;//pencolor;
BB := TBitmapLayer.Create(ImgView.Layers);
try
BB.Bitmap.DrawMode := dmTransparent;
BB.Bitmap.SetSize(imwidth,imheight);
BB.Bitmap.Canvas.Pen.Width := 3;
BB.Bitmap.Canvas.Pen.Color := pencolor;
BB.Location := GR32.FloatRect(0, 0, imwidth, imheight);
BB.Scaled := true;
BB.Tag:=3;
//// Selection:=BB; // if I use this then I cant draw because the entire layer is selected and the mouseDown event works as a mover/resizer
// BB.OnMouseDown := DrLayerMouseDown;
// BB.OnMouseUp := DrLayerMouseUp;
// BB.OnMouseMove := DrLayerMouseMove;
// BB.OnPaint := DrLayerOnPaint;
RBLayer:=nil;
EdLayerIndex.Text:=IntToStr(BB.Index);
finally
BB.Free;
end;
FDrawingLine := false;
// swapBuffers32; // needed when mouse events are active
end;
EdLayerIndex - это EditBox, где я отображаю индекс созданного / выбранного слоя (для отладки)
- Как вы можете видеть выше, если я продолжу
Selection:=BB
а такжеRBLayer:=nil
тогда DrawingLayer является только подвижным и изменяемого размера, так что это не очень хорошее решение, так как я хочу использовать события Mouse в этом конкретном слое для рисования. Если я прокомментирую только
RBLayer:=nil
сохраняяSelection:=BB
тогда DrawingLayer больше не может быть перемещен, но я не могу выбрать другие слои, которые находятся под DrawingLayer. Я могу получить доступ только к верхнему слою (последний добавленный слой PNG)Если я прокомментирую
Selection:=BB
тогда я не могу выбрать другие слои с помощью мыши. Так что в моем случае я объявил 2 слоя PNG перед моим DrawingLayer и один после него. Во время выполнения я могу выбрать только последний слой (тот, что выше 'DrawingLayer), так что это тоже не решение.
Как я могу сделать так, чтобы, когда я нажимал на слой рисования (или выбирал его иначе, как в окне списка или что-то в этом роде), DrawingLayer не был бы подвижным, но мои события Mouse Mouse сработали? И все это, когда я могу уйти от DrawingLayer, когда захочу, и выбрать другие слои, чтобы перемещаться и играть с ними. Поэтому в основном мне нужен определенный слой, чтобы НЕ действовать как другие слои.
Чего я хочу добиться, так это иметь классическое поведение в стиле Photoshop или paint.net с использованием graphics32. И это очень запутанно, как эти свойства слоя на самом деле работают.
До сих пор я выяснил, как рисовать (линии, круги, прямоугольники) на прозрачном слое динамически (используя события мыши). Так что я могу иметь слой для рисования. Рисунок происходит в моем DrLayerMouseDown
, DrLayerMouseUp
, DrLayerMouseMove
, DrLayerPaint
События. Но я не могу понять, как совместить такой слой с обычными подвижными слоями / слоями с изменяемым размером.
Остальная часть кода (как setSelection
, RBResizing
а также layerMouseDown
) в основном взят из примера слоев библиотеки graphics32.
РЕДАКТИРОВАТЬ
Чтобы проверить вашу идею с layerOptions
Я сделал следующее:
1. Запущен новый тестовый проект с ImgView и кнопкой
2. При создании я использовал тот же код, что и раньше
3.OnButtonClick Я добавил ОДИН слой, используя модифицированный AddDrawingLayer, например так:
...
BB.Scaled := true;
Selection:=BB;
Selection.LayerOptions:=Selection.LayerOptions and (not LOB_MOUSE_EVENTS); // I also tried it with BB instead of Selection
BB.OnMouseDown := DrLayerMouseDown;
BB.OnMouseUp := DrLayerMouseUp;
BB.OnMouseMove := DrLayerMouseMove;
BB.OnPaint := DrLayerOnPaint;
...
ожидая, что он станет нечувствительным к событиям мыши. Но слой все еще подвижен вместо того, чтобы быть нечувствительным к мыши. Так что я вроде ничего не делал
Поэтому я не думаю, что это помогает мне использовать эту опцию, если я не делаю это неправильно. Так что при создании слоя эта опция, похоже, не придерживается. Но если я отключу события мыши для всех слоев, один за другим, как в следующем редактировании, тогда слой чертежа будет отключен (события мыши)
РЕДАКТИРОВАТЬ
Также я попробовал другой тестовый проект с той же идеей: тот же onCreate и onButtonClick. Я добавляю 3 слоя (используя пример библиотеки Layers), каждый из которых содержит изображение (на этот раз без слоя для рисования, чтобы было проще). Затем я добавил новую кнопку, где, если вы нажмете на нее, будет выполнен следующий код:
for i := 0 to ImgView.Layers.Count-1 do
(ImgView.Layers.Items[i] as TPositionedLayer).LayerOptions:= (ImgView.Layers.Items[i] as TPositionedLayer).LayerOptions and (not LOB_MOUSE_EVENTS);
Моей целью было сделать все слои нечувствительными к событиям мыши. Мне удалось, после нажатия новой кнопки, слои больше не могли быть выбраны, однако, когда я захотел повторно включить события мыши для слоев (добавив третью кнопку со следующим кодом onClick):
for i := 0 to ImgView.Layers.Count-1 do
(ImgView.Layers.Items[i] as TPositionedLayer).LayerOptions:= (ImgView.Layers.Items[i] as TPositionedLayer).LayerOptions and (LOB_MOUSE_EVENTS);
Не было отображено никакой ошибки, но когда я попытался выбрать слой для его перемещения... все изображения слоев исчезли из вида... оставив меня с белым фоном пустым ImgView.
Что я делаю неправильно? Чтобы сделать то, что вы предложили с LayerOptions, мне нужно иметь возможность отключить события мыши для всех слоев и включить события мыши для определенного слоя, а затем, когда редактирование завершено, мне нужно иметь возможность повторно включить события мыши для всех слоев, но я делаю это неправильно, я думаю.
1 ответ
Следующие элементы влияют на события мыши
Layers.MouseEvents (логическое). Layers - это TLayerCollection TCustomImage32, который управляет слоями. Если MouseEvents имеет значение False, события мыши не распространяются на слои.
Layers.MouseListener (TCustomLayer). Слой, который "захватывает" события мыши между левой кнопкой MouseDown и MouseUp. "Захватывает" в квотах, потому что это не захват мыши, как это понимается в контексте Windows.
Слои Вариант Биты. Каждый слой имеет 32-битное свойство LayerOptions. Интересным является бит LOB_MOUSE_EVENTS (бит 29), который указывает, реагирует ли слой на события мыши. Уровень может также указывать бит LOB_NO_CAPTURE (бит 27), который предотвращает события мыши, даже если установлен LOB_MOUSE_EVENTS.
Индекс слоя. Слои проверяются (в порядке сверху вниз и ниже) на бит опции LOB_MOUSE_EVENTS. Когда слой найден с этим битом, координаты X и Y проверяются в функции HitTest для слоев. Если координаты X и Y находятся в пределах расположения слоев, HitTest завершается успешно. Результат встроенного HitTest может быть переопределен в вашем собственном событии OnHitTest. Наконец, если параметры слоев не содержат бит LOB_NO_CAPTURE, вызывается событие Layer MouseDown.
Исходя из предыдущего, я предлагаю, чтобы, когда пользователь входит в режим "редактирования", вы отключили все другие слои, кроме слоя рисования, установив их LayerOptions так, чтобы они не включали бит LOB_MOUSE_EVENTS
Layer.LayerOptions := Layer.LayerOptions and (not LOB_MOUSE_EVENTS);
Дополнительная информация об использовании слоев доступна здесь
редактировать
Для управления LOB_MOUSE_EVENTS создайте, например, что-то вроде следующего
procedure TForm7.LayerMouseDisEnable(Enable: boolean);
var
i: integer;
Lo: cardinal;
begin
for i := 0 to ImgView.Layers.Count-1 do
begin
Lo := ImgView.Layers.Items[i].LayerOptions;
if Enable then
ImgView.Layers.Items[i].LayerOptions := Lo or LOB_MOUSE_EVENTS
else
ImgView.Layers.Items[i].LayerOptions := Lo and (not LOB_MOUSE_EVENTS);
end;
end;
Вызовите это (с False), чтобы отключить события мыши в слоях непосредственно перед созданием слоя для рисования. В слое рисования будут включены события мыши, поскольку во вновь созданных слоях установлены как LOB_VISIBLE, так и LOB_MOUSE_EVENTS. Вызовите снова (с True), когда вы остановите рисование, чтобы включить событие мыши.