From 257854c587f0876a912cfbeb4fe0a30970d25a9b Mon Sep 17 00:00:00 2001
From: whiteshark0 <whiteshark0@b956fd51-792f-4845-bead-9b4dfca2ff2c>
Date: Sun, 31 May 2009 14:10:42 +0000
Subject: merged (experimental) mouse support patch by d0ccrazy some changes to
 patch   - implemented software cursor (texturable)   - option to turn of
 mouse support or switch between hardware and software cursor   - hide
 software cursor if there is no mouse activity for 5 seconds   - soft fade in
 and out for software cursor   - some test cursor-textures for deluxe theme
 (mog pls change these horible looking images)

git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1789 b956fd51-792f-4845-bead-9b4dfca2ff2c
---
 src/menu/UDisplay.pas | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/menu/UMenu.pas    | 119 +++++++++++++++++++++++++++++--
 2 files changed, 303 insertions(+), 7 deletions(-)

(limited to 'src/menu')

diff --git a/src/menu/UDisplay.pas b/src/menu/UDisplay.pas
index 58c416db..10a80c00 100644
--- a/src/menu/UDisplay.pas
+++ b/src/menu/UDisplay.pas
@@ -60,6 +60,17 @@ type
 
       OSD_LastError: string;
 
+      { software cursor data }
+      Cursor_X:            Double;
+      Cursor_Y:            Double;
+      Cursor_Pressed:      Boolean;
+      Cursor_HiddenByScreen: Boolean; // hides software cursor and deactivate auto fade in
+
+      // used for cursor fade out when there is no movement
+      Cursor_Visible:      Boolean;
+      Cursor_LastMove:     Cardinal;
+      Cursor_Fade:         Boolean;
+
       procedure DrawDebugInformation;
     public
       NextScreen:          PMenu;
@@ -78,11 +89,28 @@ type
       procedure SaveScreenShot;
 
       function  Draw: boolean;
+
+      { sets SDL_ShowCursor depending on options set in Ini }
+      procedure SetCursor;
+
+      { called when cursor moves, positioning of software cursor }
+      procedure MoveCursor(X, Y: Double; Pressed: Boolean);
+
+      
+      { draws software cursor }
+      procedure DrawCursor;
   end;
 
 var
   Display:          TDisplay;
 
+const
+  { constants for software cursor effects
+    time in milliseconds }
+  Cursor_FadeIn_Time = 500;      // seconds the fade in effect lasts
+  Cursor_FadeOut_Time = 2000;    // seconds the fade out effect lasts
+  Cursor_AutoHide_Time = 5000;   // seconds until auto fade out starts if there is no mouse movement
+
 implementation
 
 uses
@@ -125,6 +153,15 @@ begin
 
   //Set LastError for OSD to No Error
   OSD_LastError := 'No Errors';
+
+  // software cursor default values
+  Cursor_LastMove := SDL_GetTicks;
+  Cursor_Visible  := false;
+  Cursor_Pressed  := false;
+  Cursor_X        := -1;
+  Cursor_Y        := -1;
+  Cursor_Fade     := false;
+  Cursor_HiddenByScreen := true;
 end;
 
 destructor TDisplay.Destroy;
@@ -306,6 +343,160 @@ begin
     if ((Ini.Debug = 1) or (Params.Debug)) and (S = 1) then
       DrawDebugInformation;      
   end; // for
+
+  if not BlackScreen then
+    DrawCursor;
+end;
+
+{ sets SDL_ShowCursor depending on options set in Ini }
+procedure TDisplay.SetCursor;
+  var
+    Cursor: Integer;
+begin
+  Cursor := 0;
+
+  if (CurrentScreen <> @ScreenSing) or (Cursor_HiddenByScreen) then
+  begin // hide cursor on singscreen
+    if (Ini.Mouse = 0) and (Ini.FullScreen = 0) then
+      // show sdl (os) cursor in window mode even when mouse support is off
+      Cursor := 1
+    else if (Ini.Mouse = 1) then
+      // show sdl (os) cursor when hardware cursor is selected
+      Cursor := 1;
+
+    if (Ini.Mouse <> 2) then
+      Cursor_HiddenByScreen := false;
+  end
+  else if (Ini.Mouse <> 2) then
+    Cursor_HiddenByScreen := true;
+
+
+  SDL_ShowCursor(Cursor);
+
+  if (Ini.Mouse = 2) then
+  begin
+    if Cursor_HiddenByScreen then
+    begin
+      // show software cursor
+      Cursor_HiddenByScreen := false;
+      Cursor_Visible := false;
+      Cursor_Fade := false;
+    end
+    else if (CurrentScreen = @ScreenSing) then
+    begin
+      // hide software cursor in singscreen
+      Cursor_HiddenByScreen := true;
+      Cursor_Visible := false;
+      Cursor_Fade := false;
+    end;
+  end;
+end;
+
+{ called when cursor moves, positioning of software cursor }
+procedure TDisplay.MoveCursor(X, Y: Double; Pressed: Boolean);
+var
+  Ticks: Cardinal;
+begin
+  if (Ini.Mouse = 2) and ((X <> Cursor_X) or (Y <> Cursor_Y) or (Pressed <> Cursor_Pressed)) then
+  begin
+    Cursor_X := X;
+    Cursor_Y := Y;
+    Cursor_Pressed := Pressed;
+
+    Ticks := SDL_GetTicks;
+
+    if not Cursor_Visible then
+    begin
+      if (Cursor_Fade) then // we use a trick here to consider progress of fade out
+        Cursor_LastMove := Ticks - round(Cursor_FadeIn_Time * (1 - (Ticks - Cursor_LastMove)/Cursor_FadeOut_Time))
+      else
+        Cursor_LastMove := Ticks;
+
+      Cursor_Visible := True;
+      Cursor_Fade := True;
+    end
+    else if not Cursor_Fade then
+    begin
+      Cursor_LastMove := Ticks;
+    end;
+  end;
+end;
+
+{ draws software cursor }
+procedure TDisplay.DrawCursor;
+  var
+    Alpha: Single;
+    Ticks: Cardinal;
+begin
+  if (Ini.Mouse = 2) then
+  begin // draw software cursor
+    Ticks := SDL_GetTicks;
+
+    if (Cursor_Visible) and (Cursor_LastMove + Cursor_AutoHide_Time <= Ticks) then
+    begin // start fade out after 5 secs w/o activity
+      Cursor_Visible := False;
+      Cursor_LastMove := Ticks;
+      Cursor_Fade := True;
+    end;
+    
+    // fading
+    if (Cursor_Fade) then
+    begin
+      if (Cursor_Visible) then
+      begin // fade in
+        if (Cursor_LastMove + Cursor_FadeIn_Time <= Ticks) then
+          Cursor_Fade := False
+        else
+          Alpha := sin((Ticks - Cursor_LastMove) * 0.5 * pi / Cursor_FadeIn_Time) * 0.7;
+      end
+      else
+      begin //fade out
+        if (Cursor_LastMove + Cursor_FadeOut_Time <= Ticks) then
+          Cursor_Fade := False
+        else
+          Alpha := cos((Ticks - Cursor_LastMove) * 0.5 * pi / Cursor_FadeOut_Time) * 0.7;
+      end;
+    end;
+
+    // no else if here because we may turn off fade in if block
+    if not Cursor_Fade then
+    begin
+      if Cursor_Visible then
+        Alpha := 0.7 // alpha when cursor visible and not fading
+      else
+        Alpha := 0;  // alpha when cursor is hidden
+    end;
+
+    if (Alpha > 0) and (not Cursor_HiddenByScreen) then
+    begin
+      glColor4f(1, 1, 1, Alpha);
+      glEnable(GL_TEXTURE_2D);
+      glEnable(GL_BLEND);
+      glDisable(GL_DEPTH_TEST);
+
+      if (Cursor_Pressed) and (Tex_Cursor_Pressed.TexNum > 0) then
+        glBindTexture(GL_TEXTURE_2D, Tex_Cursor_Pressed.TexNum)
+      else
+        glBindTexture(GL_TEXTURE_2D, Tex_Cursor_Unpressed.TexNum);
+
+      glBegin(GL_QUADS);
+        glTexCoord2f(0, 0);
+        glVertex2f(Cursor_X, Cursor_Y);
+
+        glTexCoord2f(0, 1);
+        glVertex2f(Cursor_X, Cursor_Y + 32);
+
+        glTexCoord2f(1, 1);
+        glVertex2f(Cursor_X + 32, Cursor_Y + 32);
+
+        glTexCoord2f(1, 0);
+        glVertex2f(Cursor_X + 32, Cursor_Y);
+      glEnd;
+
+      glDisable(GL_BLEND);
+      glDisable(GL_TEXTURE_2D);
+    end;
+  end;
 end;
 
 procedure TDisplay.SaveScreenShot;
diff --git a/src/menu/UMenu.pas b/src/menu/UMenu.pas
index ceb4ac77..7d8bdce8 100644
--- a/src/menu/UMenu.pas
+++ b/src/menu/UMenu.pas
@@ -35,6 +35,7 @@ interface
 
 uses
   gl,
+  SDL,
   SysUtils,
   UTexture,
   UMenuStatic,
@@ -61,7 +62,7 @@ type
 
       ButtonPos:        integer;
       Button:           array of TButton;
-     
+
       SelectsS:         array of TSelectSlide;
       ButtonCollection: array of TButtonCollection;
     public
@@ -72,6 +73,7 @@ type
 
       Fade:       integer; // fade type
       ShowFinish: boolean; // true if there is no fade
+      RightMbESC: boolean; // true to simulate ESC keypress when RMB is pressed
 
       destructor Destroy; override;
       constructor Create; overload; virtual;
@@ -82,7 +84,7 @@ type
       function WideCharUpperCase(wchar: WideChar) : WideString;
       function WideStringUpperCase(wstring: WideString) : WideString;
       procedure AddInteraction(Typ, Num: integer);
-      procedure SetInteraction(Num: integer);
+      procedure SetInteraction(Num: integer); virtual;
       property Interaction: integer read SelInteraction write SetInteraction;
 
       //Procedure Load BG, Texts, Statics and Button Collections from ThemeBasic
@@ -145,9 +147,10 @@ type
       function DrawFG: boolean; virtual;
       function Draw: boolean; virtual;
       function ParseInput(PressedKey: cardinal; CharCode: WideChar; PressedDown : boolean): boolean; virtual;
-      // FIXME: ParseMouse is not implemented in any subclass and not even used anywhere in the code
-      //   -> do this before activation of this method
-      //function ParseMouse(Typ: integer; X: integer; Y: integer): boolean; virtual; abstract;
+      function ParseMouse(MouseButton: Integer; BtnDown: Boolean; X, Y: integer): boolean; virtual;
+      function InRegion(X1, Y1, W, H, X, Y: real): Boolean;
+      function InteractAt(X, Y: real): Integer;
+      function CollectionAt(X, Y: real): Integer;
       procedure onShow; virtual;
       procedure onShowFinish; virtual;
       procedure onHide; virtual;
@@ -167,8 +170,11 @@ type
   end;
 
 const
-  pmMove    = 1;
-  pmClick   = 2;
+  MENU_MDOWN = 8;
+  MENU_MUP = 0;
+
+  pmMove = 1;
+  pmClick = 2;
   pmUnClick = 3;
 
   iButton           = 0; // interaction type
@@ -221,6 +227,8 @@ begin
   ButtonPos := -1;
 
   Background := nil;
+
+  RightMbESC := True;
 end;
 {
 constructor TMenu.Create(Back: string);
@@ -1595,6 +1603,103 @@ begin
   Result := true;
 end;
 
+function TMenu.ParseMouse(MouseButton: Integer; BtnDown: Boolean; X, Y: integer): boolean;
+var
+  nBut: Integer;
+begin
+  //default mouse parsing: clicking generates return keypress,
+  //  mousewheel selects in select slide
+  //override ParseMouse to customize
+  Result := true;
+
+  if RightMbESC and (MouseButton = SDL_BUTTON_RIGHT) and BtnDown then begin
+    //if RightMbESC is set, send ESC keypress
+    Result:=ParseInput(SDLK_ESCAPE, #0, True);
+  end;
+
+  nBut := InteractAt(X, Y);
+  if nBut >= 0 then begin
+    //select on mouse-over
+    if nBut <> Interaction then
+      SetInteraction(nBut);
+    if (MouseButton = SDL_BUTTON_LEFT) and BtnDown then begin
+      //click button
+      Result:=ParseInput(SDLK_RETURN, #0, True);
+    end;
+    if (Interactions[nBut].Typ = iSelectS) then begin
+      //forward/backward in select slide with mousewheel
+      if (MouseButton = SDL_BUTTON_WHEELDOWN) and BtnDown then begin
+        ParseInput(SDLK_RIGHT, #0, true);
+      end;
+      if (MouseButton = SDL_BUTTON_WHEELUP) and BtnDown then begin
+        ParseInput(SDLK_LEFT, #0, true);
+      end;
+    end;
+  end
+  else begin
+    nBut := CollectionAt(X, Y);
+    if nBut >= 0 then begin
+      //if over button collection, select first child but don't allow click
+      nBut := ButtonCollection[nBut].FirstChild - 1;
+      if nBut <> Interaction then
+        SetInteraction(nBut);
+    end;
+  end;
+end;
+
+function TMenu.InRegion(X1, Y1, W, H, X, Y: real): Boolean;
+begin
+  Result:=False;
+  X1 := X1 * Screen.w/800;
+  W := W * Screen.w/800;
+  Y1 := Y1 * Screen.h/600;
+  H := H * Screen.h/600;
+  if (X >= X1) and (X <= X1+W) and (Y >= Y1) and (Y <= Y1+H) then
+    Result := true;
+end;
+
+//takes x,y coordinates and returns the interaction number
+//of the control at this position
+function TMenu.InteractAt(X, Y: real): Integer;
+var
+  i, nBut: Integer;
+begin
+  Result:=-1;
+  for i:=Low(Interactions) to High(Interactions) do begin
+    case Interactions[i].Typ of
+      iButton:if InRegion(Button[Interactions[i].Num].X, Button[Interactions[i].Num].Y, Button[Interactions[i].Num].W, Button[Interactions[i].Num].H, X, Y) and
+           Button[Interactions[i].Num].Visible then begin
+          Result:=i;
+          exit;
+        end;
+      iBCollectionChild:if InRegion(Button[Interactions[i].Num].X, Button[Interactions[i].Num].Y, Button[Interactions[i].Num].W, Button[Interactions[i].Num].H, X, Y) then begin
+          Result:=i;
+          exit;
+        end;
+      iSelectS:if InRegion(SelectSs[Interactions[i].Num].X, SelectSs[Interactions[i].Num].Y, SelectSs[Interactions[i].Num].W, SelectSs[Interactions[i].Num].H, X, Y) or
+        InRegion(SelectSs[Interactions[i].Num].TextureSBG.X, SelectSs[Interactions[i].Num].TextureSBG.Y, SelectSs[Interactions[i].Num].TextureSBG.W, SelectSs[Interactions[i].Num].TextureSBG.H, X, Y) then begin
+          Result:=i;
+          exit;
+        end;
+    end;
+  end;
+end;
+
+//takes x,y coordinates and returns the button collection id
+function TMenu.CollectionAt(X, Y: real): Integer;
+var
+  i, nBut: Integer;
+begin
+  Result:=-1;
+  for i:=Low(ButtonCollection) to High(ButtonCollection) do begin
+    if InRegion(ButtonCollection[i].X, ButtonCollection[i].Y, ButtonCollection[i].W, ButtonCollection[i].H, X, Y) and
+        ButtonCollection[i].Visible then begin
+      Result:=i;
+      exit;
+    end;
+  end;
+end;
+
 procedure TMenu.SetAnimationProgress(Progress: real);
 begin
   // nothing
-- 
cgit v1.2.3