Jak sprawdzić czy kursor myszki jest poza obszarem komponentu?

0

Po najechaniu myszką na jakiś element zmienia on kolor, po zjechaniu kolor wraca do poprzedniego. Jednak kiedy ruszę myszką bardzo szybko i wyjadę poza okno to kolor elementu nie wraca do początkowego. W jaki sposób można rozpoznać, że myszka jest poza oknem i wtedy zmienić kolor elementów.
FormMouseLeave niestety też nie reaguje, przy szybkim przesunięciu.

1

Czy ja mam deja vu?
Kursor nie powraca do oryginalnego kształtu przy szybszych ruchach
I musi reagować tylko nie FormMouseLeave bo z jakiej racji tylko MouseLeave dla danego komponentu.

0

Normalnie reaguje, ale kiedy myszkę zabiorę poza okno, to wtedy panel nie zmienia koloru. Kursor też zmieniałem. I akurat kursor wraca do tej normalnej postaci, tylko kolor elementu zostaje.

Używam MouseLeave dla komponentu, ale widząc, że po wyjechaniu za okno się nie zmienia chciałem spróbować z FormMouseLeave. Myślałem, że komputer odczyta to tak, że myszka jest już po za oknem więc wszystkie elementy mają zmienić kolor na wyjściowy. Ale to też nie zadziałało.

1

Pokaż kod bo skoro kursor wraca do normalnego to znaczy że komponent komunikat dostaje i wszystko powinno być ok a tylko ty coś nawydziwiałeś.

0

Jak sprawdzić czy myszka jest poza oknem?

@lucasp17 - mysz masz zawsze obok komputera, więc tego sprawdzać nie musisz;

Jeśli dany komponent przechwytuje i obsługuje komunikaty myszy, to on musi sam siebie przemalowywać, a nie klasa formularza; Owszem, zdaża się, że kursor przesunie się tak szybko, że komunikat opuszczenia powierzchni komponentu nie zostanie wysłany, czego efektem jest brak deaktywacji komponentu; Są to bardzo rzadkie przypadki, ale można sobie z nimi poradzić;

Najlepiej by było jakbyś pokazał kod, bo nawet na moim leciwym sprzęcie, oprogramowanie np. TImage w dokładnie taki sam sposób jak pokazał @kAzek, nie powoduje takich błędów; Oczywiście można to łatwo zepsuć, jeśli obsługa komunikatu CM_MOUSEENTER zawiera pierdyliard instrukcji, które wykonują się relatywnie długo, przez co zanim się one wykonają - kursor może już być poza komponentem; Ale na to także jest sporo lekarstw, np. wprowadzenie timeoutu w CM_MOUSEENTER i sprawdzenie po określonym czasie, czy kursor nadal jest nad komponentem; Jeśli tak - wykonujemy wizualną aktywację komponentu, a jeśli nie - nic nie robimy.

0
procedure TFormBaza.Image1MouseEnter(Sender: TObject);
begin
  Image1.Visible := False;
  Image2.Visible := True;
end; 
procedure TFormBaza.Image2MouseLeave(Sender: TObject);
begin
  Image1.Visible := True;
  Image2.Visible := False;
end; 

To fragment o który mi chodzi. Być może to nie jest dobre rozwiązanie tego co chciałem osiągnąć. Kursor obu TImage jest ustawiony we właściwościach na crHandPoint. Po najechaniu ładuje się Image2, kursor też się zmienia. Po zjechaniu kursor jest normalny, obrazek też. Ale gdy szybko wyjadę po za okno, to wtedy obrazek zostaje na Image2, choć też nie zawsze. Początkowe parametry mają takie same, tylko Image2 ma Visible = False.

A z tą myszką to już nie przesadzaj. Wiadomo, że chodzi o kursor, a wiadomo też jak się potocznie mówi.

0

Nie wiem jak inni ale ja nadal nie wiem o co w tym ma chodzić... Co chcesz osiągnąć dlaczego obsługujesz Enter jednego Image a Leave drugiego?

0

Image1 ma Visible na True. A Image2 na False. Po najechaniu na Image1, ma się on robić False, a Image2 True. A więc po najechaniu na Image1 kursor tak na prawdę ma być na Image2. Po zjechaniu z Image2, znowu wartości Visible mają być zamieniona, tak by widoczny był Image1.

3

Jeżeli masz na myśli to co ja to po pierwsze trzeba wykorzystać fakt że zdarzenia OnMouseEneter i OnMouseLeave są takie same TNotifyEvent a więc nie piszesz dla każdego z osobna bo to bez sensu tylko oba zdarzenia (MouseEnter i MouseLeave) dla obu komponentów (Image1 i Image2) podpinasz pod tą samą procedurę (możesz ją sobie nazwać jak chcesz ja w przykładzie nie zmieniam nazwy zostawiam domyślną wygenerowana przez Delphi) a kod procedury jest taki:

procedure TForm1.Image1MouseEnter(Sender: TObject);
begin
  Image1.Visible:= not Image1.Visible;
  Image2.Visible:= not Image2.Visible;
end;
0

Czy przy takim kodzie nie trzeba już używać MouseLeave?

0

Ok, faktycznie. Teraz wszystko działa. Kod się znacznie skróci jak to poprawie we wszystkich miejscach. Dzięki.

0

Ale chwila. Bo to tak na prawdę nie rozwiązało problemu. Miejsce w kodzie jest zaoszczędzone to fakt, ale nadal przy szybkim przesunięciu myszy obrazek się nie zmienia.

0

To wrzucę już wieczorem, po powrocie. A co do tego drugiego. Wiem, że przy dodaniu zdarzenia onClick coś się zmieni, ale mi chodziło o ten kod podany przez Ciebie. W nim po naciśnięciu nic się nie może zmienić... Właśnię widzę, że mój komentarz był bardzo nieprecyzyjny. Mogło z niego wynikać, że po naciśnięciu obrazka panel ma się pojawiać i znikać. A ma się tylko pojawić. Ale po naciśnięciu Image2, to Image2.Visible ma być False, a Image1 True.

Ja to rozwiązałem w ten sposób:

procedure TFormEdytor.Image2Click(Sender: TObject);
begin
  Image1.Visible := True;
  Image2.Visible := False;
  Panel1.Visible := True;
end;

procedure TFormEdytor.Image1(Sender: TObject);
begin
  if Panel1.Visible <> True then
    begin
      Image1.Visible := not Image1.Visible;
      Image2.Visible := not Image2.Visible;
    end;
end;

Tylko czy jest to rozwiązanie właściwe?

0

Jeśli Ci to działa dokładnie tak, jak tego oczekujesz to jest właściwe; Ewentualnie zamiast bawić się w przypisywanie stanu widoczności komponentów przez właściwość Visible, możesz skorzystać z ich metod Show i Hide - będzie czytelniej:

procedure TFormEdytor.Image2Click(Sender: TObject);
begin
  Image2.Hide();
  Image1.Show();
  Panel1.Show();
end;
 
procedure TFormEdytor.Image1(Sender: TObject);
begin
  if not Panel1.Visible then
  begin
    Image1.Visible := not Image1.Visible;
    Image2.Visible := not Image2.Visible;
  end;
end;

Blokowi begin end nadawaj takie samo wcięcie, jak instrukcji if.

0

Działa poprawnie, ale nadal to wyjeżdżanie poza okno nie zmienia obrazka. A to ma znaczenie jakie wcięcie ma begin end? Mi się wydaje to czytelniejsze, bo od razu widzę, że ten fragment zależy od if.

0

Działa poprawnie, ale nadal to wyjeżdżanie poza okno nie zmienia obrazka.

Sam sobie przeczysz; Albo coś działa jak należy, albo nie działa, więc skoro działa poprawnie, to jakim cudem nie zmienia się obrazek?

Wcięcia nie mają znaczenia dla samego kompilatora, bo i tak je pomija podczas analizy kodu, ale dzięki wcięciom i wielu innym elementom wchodzącym w skład formatowania kodu, ten kod staje się bajecznie czytelny i bardzo łatwy do analizy; Czytelny kod ma służyć programiście, a kompilator poradzi sobie z największym bajzlem w kodzie.

0

To się Ci źle wydaje. Musisz tak formatować kod, jak pokazał @furious programming lub robić to wbudowanym w Lazarusa formatterem JEDI, który również będzie domyślnie tak wykonyał wcięcia. Jeżeli chcesz żeby ktokolwiek traktował Ciebie poważnie i chciał patrzeć na jakiś Twój kod. To musi być on koniecznie sformatowany. I tyle. Poza tym zatytułowałeś wątek tak, że sugeruje on iż chcesz sprawdzić czy myszka jest poza oknem głownym czy tam formatką. A wtedy należało by to zrobić za pewne tak pod LCL:

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Windows, LMessages, Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs;

type
  { TForm1 }

  TForm1 = class(TForm)
  private
  public
    procedure CmMouseEnter(var Msg : TMessage); message CM_MOUSEENTER;
    procedure CmMouseLeave(var Msg : TMessage); message CM_MOUSELEAVE;
  end;

var
  Form1 : TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.CmMouseEnter(var Msg : TMessage);
begin
  Caption := 'IN';
end;

procedure TForm1.CmMouseLeave(var Msg : TMessage);
begin
  Caption := 'OUT';
end;

end.

EDIT: tytuł tematu dopasowałem do jego zawartośći. Bo chodzi o kursor myszki, a nie fizycznie samą myszkę. I za pewne chodzi o komponent, a nie okno aplikacji. Chociaż również ono jest oczywiście komponentem. Jeżeli piszemy z użyciem VCL czy tam LCL.

0

Trudno żeby działało, skoro masz zdarzenie OnMouseEnter podpięte także pod OnMouseLeave... o.O

0
kAzek napisał(a):

Jeżeli masz na myśli to co ja to po pierwsze trzeba wykorzystać fakt że zdarzenia OnMouseEneter i OnMouseLeave są takie same TNotifyEvent a więc nie piszesz dla każdego z osobna bo to bez sensu tylko oba zdarzenia (MouseEnter i MouseLeave) dla obu komponentów (Image1 i Image2) podpinasz pod tą samą procedurę (możesz ją sobie nazwać jak chcesz ja w przykładzie nie zmieniam nazwy zostawiam domyślną wygenerowana przez Delphi) a kod procedury jest taki:

procedure TForm1.Image1MouseEnter(Sender: TObject);
begin
  Image1.Visible:= not Image1.Visible;
  Image2.Visible:= not Image2.Visible;
end;

Zastosowałem się do tego.

0

Testuj aplikację w trybie release, a nie pod debugerem, bo przez niego program będzie działał wolniej; U mnie też tak się dzieje, nawet jeśli zrobię to po swojemu; Komponent przy szybkim ruchu myszą nie nadąża się schować, dlatego pozostaje aktywowany;

Jest na to sposób - zamiast tworzyć dwa komponenty, stwórz jeden; Załaduj obie grafiki do pamięci, a w zdarzeniach OnMouseEnter i OnMouseLeave, za pomocą metody Assign przypisuj odpowiednią grafikę; Będzie to działało szybciej, ale jeśli i to nie pomoże, to tak jak napisałem wcześniej - będzie trzeba pokombinować z małym timeoutem i sprawdzaniem pozycji kursora na ekranie, po minięciu np. 200ms od odebrania przez komponent komunikatu.

0

Strasznie dużo by było z tym kombinowania. Bo tak na prawdę ten obrazek jest inny w zalezności od kilku różnych parametrów. I tych grafik jest znacznie więcej niż tylko dwie.

Mógłbyś wyjaśnić co znaczy "Testuj aplikację w trybie release, a nie pod debugerem" Po skompilowaniu i uruchomieniu pliku exe problem jest taki sam.

Również nie do końca zrozumiałem co miałeś na myśli w komentarzu

Powinieneś mieć jeden komponent dla jednego przycisku, który zmienia grafikę podczas otrzymania odpowiedniego komunikatu 

Chodziło procedurę? Każdy obrazek mam mieć swoją własną, o tej samej treści?

1

No dobra, łopatologicznie...

Strasznie dużo by było z tym kombinowania. Bo tak na prawdę ten obrazek jest inny w zalezności od kilku różnych parametrów. I tych grafik jest znacznie więcej niż tylko dwie.

Wcale nie było by tak dużo kombinacji; Tym bardziej, gdybyś normalnie utworzył swoją klasę komponentu, dziedziczoną np. po TImage i wszystko oprogramował wewnątrz tej klasy; Mógłbyś udostępnić normalne właściwości, do wyboru wszystkich obsługiwanych przez jeden przycisk grafik, ewentualnie dodać więcej zdarzeń, jeśli planujesz inne niż standardowe;

W każdym razie sprawa była by prostsza, bo jeden (wizualny) przycisk byłby jednym komponentem; A teraz masz dwa komponenty, zawierające po jednej grafice, więc zamiast zmieniać tylko grafikę, Ty musisz ukrywać i pokazywać zbędne komponenty; To jest raczej nie dość, że nieprofesjonalne, to jeszcze wolniejsze w działaniu i wszystko utrudnia.

0

A gdyby tak utworzyć jeden komponent i w zdarzeniu onMouseEnter odsyłać do zewnętrznej procedury, która za pomocą .Picture.LoadFromFile wczytywała by odpowiedną grafikę?

0

Nie, nie, nie - w przypadku stworzenia własnej klasy komponentu, obsługa komunikatu CM_MOUSEENTER odbywała by się wewnątrz klasy, a ewentualnie dodatkowo wykonywany byłby kod zdarzenia, jeśli użytkownik chce coś dodatkowo wykonać, oprócz swapu obrazka; Tylko wtedy nie wykonywałbyć ładowania obrazka z dysku, tylko wykorzystywał obrazki wskazane w Object Inspektor'ze;

Zobacz sobie w sieci na kursy tworzenia własnych komponentów - nie jest to trudne, ale trochę trzeba poczytać; Tym bardziej, jeśli się dobrze nie zna programowania obiektowego.

1

Co do tworzenia komponentów, to dawałem mu już przykładowy kod w tym postcie http://4programmers.net/Forum/Newbie/160852-czy_mozna_nadac_dowolny_wyglad_komponentom_w_lazarusie?p=1028379#id1028379 także wystarczy wywalić co zbędne i połączyć to z moim kodem pare postów wyżej w tym wątku i z TFrom1 wiadomo zmienić na nazwe bazową swojej kontrolki. Ładowanie można zrobić poprzez dodanie swoich własności. Albo można trzymać sobie w tablicy we właściwym kodzie i rzutować na przykład na własności kompnentu Tag. Ponieważ wiele osób zapomina, że można w niej przechować nie tylko liczbę typu integer. Ale również śmiało bez problemów różne obiekty.

0

Damy radę, dzięki.

0

Rzeczywiście czasami się "zacina" pod Lazarusem a ten sam kod w Delphi działa bez problemu.
Tymczasowo testowałem z timerem "wspomagającym" (żadne MouseMove formy itp. nie pomaga) i niby działa ale to nie jest najlepsze rozwiązanie jednak na razie nie mam innego pomysłu (a Lazarus mnie trochę rozczarował). Sprawdzanie nawet w Timerze Image2.MouseEntered też nie działa dobrze dlatego aż tyle kodu:

procedure TForm1.OnTimer(Sender: TObject);
var
  p: TPoint;
begin
  if Image2.Visible then
  begin
    GetCursorPos(p);
    p:= Image2.ScreenToClient(p);
    if not PtInRect(Image2.DestRect, p) then
      Image1MouseEnter(Image2);
  end;
end;    
0

Szczerze mówiąc czegoś takiego jeszcze nie robiłem, więc nie wiem jak to odnieść do poprzedniego kodu. Trzeba użyć ten i ten? Z czego ten nowy nie w w zdarzeniach związanych z ruchami myszki?

0

U mnie jednak to też do końca prawidłowo nie działa. Obrazek się zmienia dopiero kiedy myszkę zatrzymam i jeszcze raz przesunę, ale wtedy po powrocie na okno programu w jakimkolwiek miejscu przez chwilę widoczny jest ponownie obrazek 2.

1 użytkowników online, w tym zalogowanych: 0, gości: 1