Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Поставить каретку в место DragDrop'а  (Прочитано 12361 раз)
0 Пользователей и 1 Гость смотрят эту тему.
NeferSky
Постоялец

ua
Offline Offline
Бессмертный


« : 30-07-2018 13:53 » 

Привет всем.
Собственно... Делаю DragDrop из проводника в Memo. В меме уже есть текст, и задача - имя дропнутого файла вставить в то место в тексте, где произошел дроп. Что-то никак не могу понять, как поставить каретку (мигающую палку) в указанную точку экрана...
Попробовал такие варианты:
- TMemo.SetCaretPos выставляет каретку по координатам символов, то есть, SetCaretPos(1,2) выставит каретку в 1-ю строку перед 2-м символом. Это не то.
- Сообщение EM_CHARFROMPOS - возможно, я что-то не так делаю, но он отбрасывает каретку в начало видимого текста в меме.
У кого-нибудь есть идеи?

Собственно, кот обработки дропа:
Код:
procedure TMyForm.WMDropFiles(var Msg: TWMDropFiles);
var
  HndDrop: HDROP;
  FileName: AnsiString;
  FileNameLength: Integer;
  Pt: TPoint;

begin
  inherited;

  HndDrop := Msg.Drop;
  try
    FileNameLength := DragQueryFile(HndDrop, 0, nil, 0);
    SetLength(FileName, FileNameLength);
    DragQueryFile(HndDrop, 0, PChar(FileName), FileNameLength + 1);
    DragQueryPoint(HndDrop, Pt);
    Clipboard.AsText := FileName;

    // Тут нужна какая-то магия для установки каретки

    memHTMLCode.PasteFromClipboard; // Наверное лучше кусками нарезать... MemoTextПеред + FileName + MemoTextПосле...
  finally
    DragFinish(HndDrop);
  end;

  Msg.Result := 0;
end;
« Последнее редактирование: 30-07-2018 14:37 от NeferSky » Записан

Не тронь налаженный механизм, и он тебя не подведет.
Делать надо хорошо, а плохо - само получится.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #1 : 31-07-2018 04:15 » 

NeferSky, попробуй выделение текста - выделить с N-го символа по N-й
Записан

NeferSky
Постоялец

ua
Offline Offline
Бессмертный


« Ответ #2 : 31-07-2018 04:34 » 

Алексей++, вот весь вопрос в том, как получить N)))
Записан

Не тронь налаженный механизм, и он тебя не подведет.
Делать надо хорошо, а плохо - само получится.
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #3 : 31-07-2018 08:12 » 

А что за зверь этот редактор? Я просто в Дельфи не силён. Может враппер Вин-системного?

В любом случае, он сам ставит кретку по клику мышки. Или? Координаты курсора в момент дропа известны, ну так и послать ему их вначале, чтобы он установил каретку.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
NeferSky
Постоялец

ua
Offline Offline
Бессмертный


« Ответ #4 : 31-07-2018 10:35 » 

А что за зверь этот редактор? Я просто в Дельфи не силён. Может враппер Вин-системного?

В любом случае, он сам ставит кретку по клику мышки. Или? Координаты курсора в момент дропа известны, ну так и послать ему их вначале, чтобы он установил каретку.
Это под нужны нашего ПО сильно-сильно упрощенный html-редактор.
По клику мыши каретка ставится, это инкапсулировано, наверное... да наверное, любой вин-контрол по клику каретку ставит сам. Но в момент дропа клик мышкой не происходит же.
Координаты мыши в момент дропа известны, Вы правы. Но они известны как координаты в пикселях, относительно верхнего левого края контрола/окна/экрана - нужное подчеркнуть. А местонахождение каретки выставляется по координатам в символах относительно начала текста. Вот и... как поставить каретку по координатам мыши?

Видел вот такое решение. Судя по документации - должно работать, но у меня вставляет не в точку дропа, а в начало текста.
Код:
  L, C: Word;
  Pt: TPoint; // точка, возвращаемая DragQueryPoint(HndDrop, Pt);

  ...

  L := Memo1.Perform(EM_LINEFROMCHAR, -1, 0);
  C := LoWord(Memo1.Perform(EM_GETSEL, 0, 0)) - Memo1.Perform(EM_LINEINDEX, -1, 0);
  Pt.Y := L;
  Pt.X := C;
  Memo1.CaretPos := Pt;
Записан

Не тронь налаженный механизм, и он тебя не подведет.
Делать надо хорошо, а плохо - само получится.
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #5 : 31-07-2018 19:50 » 

Но в момент дропа клик мышкой не происходит же.

Именно клик - нет. Но есть другое событие - WM_LBUTTONUP, по которому в этом конкретном случае происходит дроп. Но может не происходить, а может происходить нечто совсем другое. Например, перед реализацией "дропа", "кликнуть" по контролу в известных в этот момент координатах.
Другими словами, клик надо самому "симулировать", чтобы контрол "думал", что по нему кликнули.

Но они известны как координаты в пикселях, относительно верхнего левого края контрола/окна/экрана - нужное подчеркнуть. А местонахождение каретки выставляется по координатам в символах относительно начала текста. Вот и... как поставить каретку по координатам мыши?

Бррр. Я подвожу курсор куда-то над текстом и кликаю. Каретка устанавливается. Единственный источних данных в этот момент - это положение курсора мышки в пиксельных координатах. Следовательно контрол может из этих данных расчитать ближайшее валидное положение каретки.
Ведь мышка не передаёт "координаты в символах", а обычные, экранные, в пикселях, которые

Координаты мыши в момент дропа известны

Я могу подбросить идею перерасчёта положения каретки как это "должно быть", но для этого надо будет залазить глубже в контрол, получать инфу о символах, формате каждого символа (если они отличаются), вычислять размеры каждого символа и тд и тп. А оно Вам надо?

Опять же, если контрол таки является враппером для винапишного окна, то тогда всё ещё проще - винапишная SetCaretPos получает как раз экранные координаты.
Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
NeferSky
Постоялец

ua
Offline Offline
Бессмертный


« Ответ #6 : 31-07-2018 23:20 » 

Именно клик - нет. Но есть другое событие - WM_LBUTTONUP, по которому в этом конкретном случае происходит дроп. Но может не происходить, а может происходить нечто совсем другое. Например, перед реализацией "дропа", "кликнуть" по контролу в известных в этот момент координатах.
Другими словами, клик надо самому "симулировать", чтобы контрол "думал", что по нему кликнули.
Как ни странно, но мое окно не получает WM_LBUTTONUP при дропе...
А вот симуляция клика - мысль оказалась точно в яблочко) Внизу приведу получившийся код.

Бррр. Я подвожу курсор куда-то над текстом и кликаю. Каретка устанавливается. Единственный источних данных в этот момент - это положение курсора мышки в пиксельных координатах. Следовательно контрол может из этих данных расчитать ближайшее валидное положение каретки.
Ведь мышка не передаёт "координаты в символах", а обычные, экранные, в пикселях, которые
Контрол может) Я вот и искал метод, который это делает.

Опять же, если контрол таки является враппером для винапишного окна, то тогда всё ещё проще - винапишная SetCaretPos получает как раз экранные координаты.
Поизучал ее... забавные результаты. Во-первых, контрол должен иметь фокус до выставления каретки - это то, о чем я сразу не подумал. Далее: у контрола есть свойство с координатами каретки CaretPos, которое действует как раз в координатах символов. Винапишная же SetCaretPos забавно выставляет каретку в пиксельные координаты, то есть, как попало - почти всегда в логически невалидных положениях - посреди буквы, или между строк, или вне текста вообще... При этом от такой каретки толку нет, потому что если нажать на клавиатуре, например, кнопку "стрелка влево" (стрелку вправо, букву - неважно) - то такая  установленная каретка пропадает, а работа с текстом продолжается с предыдущего валидного местоположения каретки.

Ну да ладно. Спасибо, Джон, что подсказал про симуляцию клика. Финальный вариант приложил. Завтра соберу проект, попробую - должно работать как надо.

Большое спасибо всем, принявшим участие.

Код:
procedure TMyForm.WMDropFiles(var Msg: TWMDropFiles);
var
  HndDrop: HDROP;
  FileName: AnsiString;
  FileNameLength: Integer;
  Pt: TPoint;

begin
  inherited;

  HndDrop := Msg.Drop;
  try
    FileNameLength := DragQueryFile(HndDrop, 0, nil, 0);
    SetLength(FileName, FileNameLength);
    DragQueryFile(HndDrop, 0, PChar(FileName), FileNameLength + 1);
    DragQueryPoint(HndDrop, Pt);

    // Особая магия для установки каретки
    mouse_event(MOUSEEVENTF_LEFTDOWN, Pt.X, Pt.Y, 0, 0);
    mouse_event(MOUSEEVENTF_LEFTUP, Pt.X, Pt.Y, 0, 0);
    Application.ProcessMessages;

    Clipboard.AsText := FileName;
    memHTMLCode.PasteFromClipboard;
  finally
    DragFinish(HndDrop);
  end;

  Msg.Result := 0;
end;
Записан

Не тронь налаженный механизм, и он тебя не подведет.
Делать надо хорошо, а плохо - само получится.
zubr
Гость
« Ответ #7 : 01-08-2018 11:08 » 

Проблема решается 2 строками кода:
1. Memo1.SetFocus;
2. Memo1.CaretPos := Memo1.ScreenToClient(ptScreen);
Где ptScreen экранные координаты курсора.
И все!!!
Записан
NeferSky
Постоялец

ua
Offline Offline
Бессмертный


« Ответ #8 : 01-08-2018 14:32 » 

Проблема решается 2 строками кода:
1. Memo1.SetFocus;
2. Memo1.CaretPos := Memo1.ScreenToClient(ptScreen);
Где ptScreen экранные координаты курсора.
И все!!!

Однако, не решает - специально только что проверил. Ну я просто так что ли рассказывал тут, что TMemo.CaretPos в символах считается, а не в пикселях...

Смотрим хелп по CaretPos http://docwiki.embarcadero.com/Libraries/XE6/en/Vcl.StdCtrls.TCustomMemo.CaretPos:
Цитата
CaretPos defines a location onscreen, in characters, with the origin in the top left corner of the memo. X specifies the horizontal coordinate of the point, Y specifies the vertical coordinate.

Смотрим исходник ScreenToClient:
Код:
function TControl.ScreenToClient(const Point: TPoint): TPoint;
var
  Origin: TPoint;
begin
  Origin := ClientOrigin;
  Result.X := Point.X - Origin.X;
  Result.Y := Point.Y - Origin.Y;
end;

Как оно должно пересчитывать координаты Screen в Characters - я даже не знаю. Это будет что-то такое... необычное...
Записан

Не тронь налаженный механизм, и он тебя не подведет.
Делать надо хорошо, а плохо - само получится.
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #9 : 01-08-2018 17:44 » 

Как ни странно, но мое окно не получает WM_LBUTTONUP при дропе...

Если всё происходит под виндой, то нет никакого иного события (ну, если перетаскивать правой кнопкой, но и в этом случае L поменяется на R, только и всего). Как протекает drag'n'drop: нажимаем левую кнопку (ессно получаем WM_LBUTTONDOWN и подготавливаем d'n'd - устанавливаем капчу сообщений, и тд и тп), дальше - тяяяяяянем (WM_MOUSEMOVE), а теперь надо "бросить", т.е. отпустить левую кнопку мышки - WM_LBUTTONUP.

Но, как я уже сказал, Дельфи - не мой конёк, и вполне возможно что сообщение обрабатывается фрэймворком контрола и не транслируется дальше. Но окошко его получает в любом случае, думаю что Spy++ может это легко подтвердить.

Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
NeferSky
Постоялец

ua
Offline Offline
Бессмертный


« Ответ #10 : 02-08-2018 07:59 » 

Если всё происходит под виндой, то нет никакого иного события (ну, если перетаскивать правой кнопкой, но и в этом случае L поменяется на R, только и всего). Как протекает drag'n'drop: нажимаем левую кнопку (ессно получаем WM_LBUTTONDOWN и подготавливаем d'n'd - устанавливаем капчу сообщений, и тд и тп), дальше - тяяяяяянем (WM_MOUSEMOVE), а теперь надо "бросить", т.е. отпустить левую кнопку мышки - WM_LBUTTONUP.

Да, это все так. Ну, в моем случае, WM_LBUTTONDOWNполучает проводник, а не мое окно, но это не принципиально. Однако, я вешал в окно обработчик WM_LBUTTONUP - он не реагирует... Вот что я подумал: WM_LBUTTONUP может, приходит только если таскать что-то в пределах своего окна, кнопку, например. А если из чужого окна - то приходит WM_DROPFILES, а дальше крутись как хочешь)) Наверное, ДрегДроп между окнами разных программ полностью контролирует винда?
Записан

Не тронь налаженный механизм, и он тебя не подведет.
Делать надо хорошо, а плохо - само получится.
Джон
просто
Администратор

de
Offline Offline
Пол: Мужской

« Ответ #11 : 02-08-2018 20:22 » 

Наверное, ДрегДроп между окнами разных программ полностью контролирует винда?

Обычно, полное управление днд "по уму" обеспечивается в одном месте. Те где инициируется, там и заканчивается.
Однако сообщения мышки получает окно, которое находится в данный момент под курсором. Поэтому есть такая замечательная ф-я SetCapture, которая обеспечивает приём мышиных сообщений тем окном, в котором установлен захват, даже если уехать мышом в Африку. Вспомните, например, поведение перетаскиваемых объектов за пределы видимости. После отпускания левой кнопки они "прыгают" обратно.

Сама винда ничего не контролирует, она только обеспечивает доставку сообщений.

А WM_DROPFILES это уже частный случай днд, который используется в основном для открытия файлов перетаскиванием.
Ессно, что днд начинается в "чужой" программе и не может быть закончен в другой, поэтому, в данном случае (если я правильно понял речь идёт о нем, а не о каком-то своём Проводнике) Проводник Винды, получая WM_LBUTTONUP, узнаёт хэндл окна в точке с координатами мышиного курсора и отправляет этому окну WM_DROPFILES.

В этом случае можно в Меме, по WM_DROPFILES сначала запросить координаты мыши GetCursorPos, а дальше по выше оговоренному сценарию.
« Последнее редактирование: 02-08-2018 20:31 от Джон » Записан

Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома.
"Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash
"Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman
"All science is either physics or stamp collecting." Ernest Rutherford
"Wer will, findet Wege, wer nicht will, findet Gründe."
SCRIBE
Гость
« Ответ #12 : 14-08-2018 19:32 » new

Еще выставление каретки в RTF тексте имеет свои ньюансы, так что проверяйте, может это тот случай.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines