Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add map thumbnail preview to multiplayer game lobby map list hover #637

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using DTAClient.Online.EventArguments;
using ClientCore.Extensions;
using TextCopy;
using System.Reflection;

namespace DTAClient.DXGUI.Multiplayer.GameLobby
{
Expand Down Expand Up @@ -101,6 +102,8 @@
}
}

private MapPreviewPanel panelMapPreview;

protected Map Map => GameModeMap?.Map;
protected GameMode GameMode => GameModeMap?.GameMode;

Expand Down Expand Up @@ -213,6 +216,13 @@
ex.Message));
}

panelMapPreview = new MapPreviewPanel(WindowManager, this.MapLoader);
panelMapPreview.Initialize();
panelMapPreview.ClearInfo();
panelMapPreview.Disable();
panelMapPreview.InputEnabled = false;
AddChild(panelMapPreview);

btnLeaveGame = FindChild<XNAClientButton>(nameof(btnLeaveGame));
btnLeaveGame.LeftClick += BtnLeaveGame_LeftClick;

Expand Down Expand Up @@ -715,6 +725,7 @@
if (lbGameModeMapList.HoveredIndex < 0 || lbGameModeMapList.HoveredIndex >= lbGameModeMapList.ItemCount)
{
mapListTooltip.Text = string.Empty;
panelMapPreview.Disable();
return;
}

Expand All @@ -724,6 +735,25 @@
mapListTooltip.Text = "Original name:".L10N("Client:Main:OriginalMapName") + " " + gmm.Map.UntranslatedName;
else
mapListTooltip.Text = string.Empty;

ShowMapPreviewPanelForIndex(lbGameModeMapList.HoveredIndex);
}
private void ShowMapPreviewPanelForIndex(int index)
{
if (index < 0 || index > lbGameModeMapList.ItemCount)
{
panelMapPreview.Disable();
return;
}
panelMapPreview.Enable();
panelMapPreview.X = lbGameModeMapList.X + lbGameModeMapList.Width;
panelMapPreview.Y = lbGameModeMapList.Y;

XNAListBoxItem item = lbGameModeMapList.GetItem(1, index);

GameModeMap gameModeMap = (GameModeMap)item.Tag;

panelMapPreview.SetInfo(gameModeMap);
}

private void PickRandomMap()
Expand Down Expand Up @@ -780,7 +810,7 @@
ddGameModeMapFilter.SelectedIndex = gameModeMapFilterIndex;
}

protected void AddSideToDropDown(XNADropDown dd, string name, string? uiName = null, Texture2D? texture = null)

Check warning on line 813 in DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs

View workflow job for this annotation

GitHub Actions / build-clients (Ares)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 813 in DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs

View workflow job for this annotation

GitHub Actions / build-clients (Ares)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 813 in DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs

View workflow job for this annotation

GitHub Actions / build-clients (TS)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 813 in DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs

View workflow job for this annotation

GitHub Actions / build-clients (TS)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 813 in DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs

View workflow job for this annotation

GitHub Actions / build-clients (YR)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 813 in DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs

View workflow job for this annotation

GitHub Actions / build-clients (YR)

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
XNADropDownItem item = new()
{
Expand Down
168 changes: 168 additions & 0 deletions DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

Check failure on line 7 in DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs

View workflow job for this annotation

GitHub Actions / build-clients (Ares)

The type or namespace name 'Forms' does not exist in the namespace 'System.Windows' (are you missing an assembly reference?)

Check failure on line 7 in DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs

View workflow job for this annotation

GitHub Actions / build-clients (TS)

The type or namespace name 'Forms' does not exist in the namespace 'System.Windows' (are you missing an assembly reference?)

Check failure on line 7 in DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs

View workflow job for this annotation

GitHub Actions / build-clients (YR)

The type or namespace name 'Forms' does not exist in the namespace 'System.Windows' (are you missing an assembly reference?)
using ClientCore.Extensions;
using Microsoft.Xna.Framework.Graphics.PackedVector;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Rampastring.XNAUI;
using Rampastring.XNAUI.XNAControls;

using DTAClient.Domain.Multiplayer;

namespace DTAClient.DXGUI.Multiplayer.GameLobby
{

/// <summary>
/// A UI panel that displays information about a hosted CnCNet or LAN game.
/// </summary>
public class MapPreviewPanel : XNAPanel
{
public MapPreviewPanel(WindowManager windowManager, MapLoader mapLoader)
: base(windowManager)
{
this.mapLoader = mapLoader;
DrawMode = ControlDrawMode.UNIQUE_RENDER_TARGET;
}

private MapLoader mapLoader;

private XNALabel lblMapPreview;
private XNALabel lblMapName;
private XNALabel lblMapMaxPlayers;

private GameModeMap map = null;

private bool disposeTextures = false;
private Texture2D mapTexture = null;
private Texture2D noMapPreviewTexture = null;

private int mapPreviewPositionY = 0;
private const int initialPanelHeight = 200;
private const int initialPanelWidth = 235;
private const int maxPreviewHeight = 150;
private const int mapPreviewMargin = 1; //border
private const int padding = 6;

public override void Initialize()
{
ClientRectangle = new Rectangle(0, 0, initialPanelWidth, initialPanelHeight);
BackgroundTexture = AssetLoader.CreateTexture(new Color(0, 0, 0, 255), 1, 1);
PanelBackgroundDrawMode = PanelBackgroundImageDrawMode.STRETCHED;

lblMapPreview = new XNALabel(WindowManager);
lblMapPreview.FontIndex = 1;
lblMapPreview.Text = "MAP PREVIEW".L10N("Client:Main:MapPreview");
AddChild(lblMapPreview);

lblMapName = new XNALabel(WindowManager);
lblMapName.FontIndex = 1;
lblMapName.Text = "";
AddChild(lblMapName);

lblMapMaxPlayers = new XNALabel(WindowManager);
lblMapMaxPlayers.FontIndex = 1;
lblMapMaxPlayers.Text = "";
AddChild(lblMapMaxPlayers);

if (AssetLoader.AssetExists("noMapPreview.png"))
noMapPreviewTexture = AssetLoader.LoadTexture("noMapPreview.png");

lblMapPreview.CenterOnParent();
lblMapPreview.ClientRectangle = new Rectangle(lblMapPreview.X, padding,
lblMapPreview.Width, lblMapPreview.Height);
lblMapName.ClientRectangle = new Rectangle(padding, lblMapPreview.Y + lblMapPreview.Height + padding,
lblMapName.Width, lblMapName.Height);
lblMapMaxPlayers.ClientRectangle = new Rectangle(padding, lblMapName.Y + lblMapName.Height + padding,
lblMapMaxPlayers.Width, lblMapMaxPlayers.Height);

base.Initialize();
}

public void SetInfo(GameModeMap map)
{
ClearInfo();

this.map = map;

lblMapPreview.Visible = true;
lblMapName.Text = map.Map.Name;
lblMapName.Visible = true;
lblMapMaxPlayers.Text = "Max players: ".L10N("Client:Main:MaxPlayers") + map.Map.MaxPlayers.ToString();

Check warning on line 96 in DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs

View workflow job for this annotation

GitHub Actions / build-clients (Ares)

The value of key Client:Main:MaxPlayers should not have leading or trailing whitespace.

Check warning on line 96 in DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs

View workflow job for this annotation

GitHub Actions / build-clients (TS)

The value of key Client:Main:MaxPlayers should not have leading or trailing whitespace.

Check warning on line 96 in DXMainClient/DXGUI/Multiplayer/GameLobby/MapPreviewPanel.cs

View workflow job for this annotation

GitHub Actions / build-clients (YR)

The value of key Client:Main:MaxPlayers should not have leading or trailing whitespace.
lblMapMaxPlayers.Y = lblMapName.Y + lblMapName.Height + padding;
lblMapMaxPlayers.Visible = true;

mapPreviewPositionY = lblMapMaxPlayers.Y + lblMapMaxPlayers.Height + padding;
this.Height = mapPreviewPositionY + maxPreviewHeight + padding;
if (mapLoader != null && map != null)
{
mapTexture = map.Map.IsPreviewTextureCached() ? map.Map.LoadPreviewTexture() : null;
if (mapTexture == null && noMapPreviewTexture != null)
{
Debug.Assert(!noMapPreviewTexture.IsDisposed, "noMapPreviewTexture should not be disposed.");
mapTexture = noMapPreviewTexture;
disposeTextures = false;
}
else
{
disposeTextures = true;
}
}
}

public void ClearInfo()
{
lblMapPreview.Visible = false;
lblMapName.Visible = false;
lblMapMaxPlayers.Visible = false;

if (mapTexture != null && disposeTextures)
{
Debug.Assert(!mapTexture.IsDisposed, "mapTexture should not be disposed.");
mapTexture.Dispose();
mapTexture = null;
}
}

public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);

if (map != null && mapTexture != null)
RenderMapPreview();
}

private void RenderMapPreview()
{
// Calculate map preview area
double xRatio = (ClientRectangle.Width - (mapPreviewMargin*2)) / (double)mapTexture.Width;
double yRatio = (ClientRectangle.Height - mapPreviewPositionY) / (double)mapTexture.Height;

double ratio = Math.Min(xRatio, yRatio); // Choose the smaller ratio for scaling
int textureWidth = (int)(mapTexture.Width * ratio);
int textureHeight = (int)(mapTexture.Height * ratio);

// Apply max height constraint
if (textureHeight > maxPreviewHeight)
{
ratio = maxPreviewHeight / (double)mapTexture.Height;
textureHeight = maxPreviewHeight;
textureWidth = (int)(mapTexture.Width * ratio); // Recalculate width to maintain aspect ratio
}

int texturePositionX = mapPreviewMargin + ((ClientRectangle.Width- (mapPreviewMargin*2)) / 2 - (textureWidth/2));
int texturePositionY = mapPreviewPositionY;

DrawTexture(
mapTexture,
new Rectangle(texturePositionX, texturePositionY, textureWidth, textureHeight),
Color.White
);
}
}
}
Loading