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), когда вы остановите рисование, чтобы включить событие мыши.

Другие вопросы по тегам