Skip to content

tip of the day #6841

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

Merged
merged 37 commits into from
May 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7a82bb0
i18n
exeea Apr 7, 2025
732ff20
corrected initial comment
exeea Apr 7, 2025
77e75b4
ident
exeea Apr 7, 2025
5139ef6
variable name
exeea Apr 7, 2025
7631968
more verbose variable names
exeea Apr 7, 2025
addf18c
enum
exeea Apr 7, 2025
0f9ccdf
dpi scale
exeea Apr 7, 2025
b47171a
size
exeea Apr 7, 2025
c9f3f04
dpi for monitor
exeea Apr 7, 2025
1144da9
constructor
exeea Apr 7, 2025
02e4325
dpi change method
exeea Apr 8, 2025
77776fe
fixed per monitor dpi calculation
exeea Apr 8, 2025
74a0ec8
reverted autoformatting
exeea Apr 8, 2025
daf0da6
scaling
exeea Apr 8, 2025
dafa339
font
exeea Apr 8, 2025
81ca888
resolution based scaling
exeea Apr 8, 2025
162292e
license and updated resolution scale factor
exeea Apr 9, 2025
235dc9e
Merge branch 'master' into tipoftheday
exeea Apr 9, 2025
1f762c0
I18n
exeea Apr 9, 2025
1ab265e
Merge branch 'master' into tipoftheday
exeea Apr 12, 2025
b8466ac
Merge branch 'master' into tipoftheday
exeea Apr 22, 2025
34b7d7c
Merge branch 'master' into tipoftheday
exeea Apr 24, 2025
235bb4e
Merge branch 'MegaMek:master' into tipoftheday
exeea Apr 26, 2025
d4738b9
corrected loop logic
exeea Apr 26, 2025
9d8b57c
Update megamek/src/megamek/common/util/TipOfTheDay.java
exeea Apr 26, 2025
668cc13
Merge branch 'master' into tipoftheday
exeea Apr 28, 2025
213cfe1
added logged warning
exeea Apr 28, 2025
27be84b
Merge branch 'tipoftheday' of https://github.com/exeea/megamek into t…
exeea Apr 28, 2025
ff29264
history.txt
exeea Apr 29, 2025
1512ef8
Merge branch 'master' into tipoftheday
exeea Apr 29, 2025
3ab76a9
Merge branch 'tipoftheday' of https://github.com/exeea/megamek into t…
exeea Apr 29, 2025
b1c6262
new approach for selecting the tip
exeea Apr 29, 2025
c9c38fe
tips in MM
exeea Apr 29, 2025
f164e0c
Merge branch 'master' into tipoftheday
exeea Apr 29, 2025
35d2469
Merge remote-tracking branch 'upstream/master' into tipoftheday
exeea May 19, 2025
8ee110a
de
exeea May 19, 2025
087edef
Merge branch 'master' into tipoftheday
HammerGS May 22, 2025
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
7 changes: 7 additions & 0 deletions megamek/i18n/megamek/client/TipOfTheDay.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
tip.1=Right-click on almost anything (units, buttons, map hexes) to see a context menu with available actions.
tip.2=Don't forget about physical attacks! Kicks, punches, and charges can be very effective, especially when ammo runs low or heat is high.
tip.3=Heat management is crucial. Firing too many weapons can lead to shutdowns or ammo explosions. Use the Heat Dial to plan your actions.
tip.4=Terrain matters! Woods provide cover, water helps dissipate heat, and elevation affects line of sight.
tip.5=Indirect fire (LRMs) can be powerful but requires a spotter with line of sight to the target.
tip.6=Running increases your movement distance but also makes your 'Mech a harder target *and* makes it harder for you to hit enemies.
tip.7=You can press "T" to switch between top view and isometric view.
1 change: 1 addition & 0 deletions megamek/i18n/megamek/client/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4807,3 +4807,4 @@ BotCommandPanel.PriorityTargetMenu.title=Priority Target
BotCommandPanel.PriorityTargetMenu.tooltip=Define a specific unit to be considered a priority target by the bot
BotCommandPanel.Victory.title=Request Victory
BotCommandPanel.Victory.tooltip=Declare the game over. Useful if the bot can't continue or the game reached an end state.
TipOfTheDay.title.text=Quick Tip:
1 change: 1 addition & 0 deletions megamek/i18n/megamek/client/messages_es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4131,3 +4131,4 @@ ForceGeneratorDialog.understrength=Más Débiles\
ForceGeneratorDialog.default=Predeterminado
ForceGeneratorDialog.noCrew=Sin tripulación
Gamemaster.Traitor=Unidad Traidora
TipOfTheDay.title.text=Consejo rápido:
1 change: 1 addition & 0 deletions megamek/i18n/megamek/client/messages_ru.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2231,3 +2231,4 @@ BotConfigDialog.princessHelp.title=Справка по Принцессе
SavePrincessDialog.question=Сохранить изменения в эту конфигурацию?
SavePrincessDialog.saveTargets=Сохранить список стратегичеких целей?
Gamemaster.Traitor=Юнит предатель
TipOfTheDay.title.text=Быстрый совет:
56 changes: 46 additions & 10 deletions megamek/src/megamek/client/ui/swing/MegaMekGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,20 @@
*/
package megamek.client.ui.swing;

import static megamek.common.Compute.d6;

import java.awt.*;
import static megamek.common.Compute.*;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.MediaTracker;
import java.awt.SystemColor;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
Expand All @@ -51,6 +62,7 @@
import java.util.Properties;
import java.util.Vector;
import java.util.zip.GZIPInputStream;

import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
Expand All @@ -61,6 +73,11 @@
import javax.swing.filechooser.FileFilter;
import javax.xml.parsers.DocumentBuilder;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import megamek.MMConstants;
import megamek.MegaMek;
import megamek.SuiteConstants;
Expand Down Expand Up @@ -89,7 +106,17 @@
import megamek.client.ui.swing.widget.SkinXMLHandler;
import megamek.client.ui.swing.widget.SkinnedJPanel;
import megamek.codeUtilities.StringUtility;
import megamek.common.*;
import megamek.common.Compute;
import megamek.common.Configuration;
import megamek.common.Game;
import megamek.common.GameType;
import megamek.common.IGame;
import megamek.common.KeyBindParser;
import megamek.common.MekFileParser;
import megamek.common.MekSummaryCache;
import megamek.common.PlanetaryConditionsUsing;
import megamek.common.Player;
import megamek.common.WeaponOrderHandler;
import megamek.common.annotations.Nullable;
import megamek.common.jacksonadapters.BotParser;
import megamek.common.options.IBasicOption;
Expand All @@ -101,17 +128,14 @@
import megamek.common.scenario.ScenarioLoader;
import megamek.common.util.EmailService;
import megamek.common.util.ImageUtil;
import megamek.common.util.TipOfTheDay;
import megamek.common.util.fileUtils.MegaMekFile;
import megamek.logging.MMLogger;
import megamek.server.IGameManager;
import megamek.server.Server;
import megamek.server.sbf.SBFGameManager;
import megamek.server.totalwarfare.TWGameManager;
import megamek.utilities.xml.MMXMLUtility;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class MegaMekGUI implements IPreferenceChangeListener {
private static final MMLogger LOGGER = MMLogger.create(MegaMekGUI.class);
Expand All @@ -131,6 +155,8 @@ public class MegaMekGUI implements IPreferenceChangeListener {
private Server server;
private IGameManager gameManager;
private CommonSettingsDialog settingsDialog;
JLabel splash;
private TipOfTheDay tipOfTheDay;

private static MegaMekController controller;

Expand Down Expand Up @@ -163,7 +189,17 @@ public void windowClosing(WindowEvent e) {
}
});

frame.setContentPane(new SkinnedJPanel(UIComponents.MainMenuBorder, 1));
tipOfTheDay = new TipOfTheDay(Messages.getString("TipOfTheDay.title.text"), "megamek.client.TipOfTheDay", frame);
frame.setContentPane(new SkinnedJPanel(UIComponents.MainMenuBorder, 1) {
@Override
public void paint(Graphics g) {
super.paint(g); // Draw background, border, and children first
// Now draw the tip on top is the splash is visible
if (splash != null && splash.isShowing() && splash.getWidth() > 0 && splash.getHeight() > 0 && tipOfTheDay != null) {
tipOfTheDay.drawTipOfTheDay((Graphics2D) g, splash.getBounds(), TipOfTheDay.Position.TOP_BORDER);
}
}
});

List<Image> iconList = new ArrayList<>();
iconList.add(frame.getToolkit()
Expand Down Expand Up @@ -279,7 +315,7 @@ private void showMainMenu() {
// displays aren't as large as their secondary displays.
Dimension scaledMonitorSize = UIUtil.getScaledScreenSize(frame);
Image imgSplash = getSplashScreen(FILENAME_MEGAMEK_SPLASHES, scaledMonitorSize.width, scaledMonitorSize.height);
JLabel splash = UIUtil.createSplashComponent(imgSplash, frame, scaledMonitorSize);
splash = UIUtil.createSplashComponent(imgSplash, frame, scaledMonitorSize);

FontMetrics metrics = hostB.getFontMetrics(loadB.getFont());
int width = metrics.stringWidth(hostB.getText());
Expand Down
91 changes: 84 additions & 7 deletions megamek/src/megamek/client/ui/swing/util/UIUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.ImageObserver;
import java.io.Serial;
import java.net.URL;
Expand All @@ -61,6 +62,8 @@ public final class UIUtil {

// The standard pixels-per-inch to compare against for display scaling
private static final int DEFAULT_DISPLAY_PPI = 96;
private static final Dimension REFERENCE_RESOLUTION = new Dimension(1920, 1080);
private static final double MINIMUM_RESOLUTION_SCALE_FACTOR = 0.5f;

/**
* The width for a tooltip displayed to the side of a dialog using one of TipXX classes.
Expand Down Expand Up @@ -521,11 +524,89 @@ public static int getScaledScreenHeight(DisplayMode currentMonitor) {
return DEFAULT_DISPLAY_PPI * monitorH / pixelPerInch;
}

/**
* Gets the DPI scale factor for the monitor containing the specified component
*
* @param component The component to check
* @return The DPI scale factor for the containing monitor
*/
public static double getMonitorScaleFactor(Component component) {
// Get the GraphicsConfiguration for the monitor containing this component
GraphicsConfiguration gc = (component != null) ? component.getGraphicsConfiguration() : null;
if (gc == null) {
return getDpiScaleFactor(component); //fallback
}
// Calculate the DPI scale for this specific monitor
AffineTransform transform = gc.getDefaultTransform();
return transform.getScaleX();
}

/**
* Gets the resolution scale factor for the monitor containing the specified component.
* Baseline is 1080p (1920x1080).
*
* @param component The component to check
* @return The resolution scale factor for the containing monitor
*/
public static double getResolutionScaleFactor(Component component) {
return getResolutionScaleFactor(component, REFERENCE_RESOLUTION);
}

/**
* Gets the resolution scale factor for the monitor containing the specified component.
*
* @param component The component to check
* @param referenceResolution The reference resolution width/height to use for scaling
* @return The resolution scale factor for the containing monitor
*/
public static double getResolutionScaleFactor(Component component, Dimension referenceResolution) {
final Dimension logicalScreenSize = UIUtil.getScaledScreenSize(component);
final double scaleFactorX = logicalScreenSize.width / referenceResolution.getWidth();
final double scaleFactorY = logicalScreenSize.height / referenceResolution.getHeight();
return Math.max(MINIMUM_RESOLUTION_SCALE_FACTOR, Math.min(scaleFactorX, scaleFactorY));
}

/**
* Calculate the DPI scale factor for a component
*
* @param component The component to get scaling information from
* @return The scaling factor based on DPI
*/
public static float getDpiScaleFactor(Component component) {
GraphicsConfiguration gc = null;
if (component != null) {
gc = component.getGraphicsConfiguration();
}
if (gc == null) {
// Fallback to default GraphicsEnvironment if component doesn't have a GraphicsConfiguration
gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
// Get screen resolution
int dpi = Toolkit.getDefaultToolkit().getScreenResolution();
// Calculate scale factor (compared to reference 96 DPI)
return (float) dpi / DEFAULT_DISPLAY_PPI;
}

/**
* @return The height of the screen taking into account display scaling
*/
public static Dimension getScaledScreenSize(Component component) {
return getScaledScreenSize(component.getGraphicsConfiguration().getDevice().getDisplayMode());
GraphicsConfiguration gc = null;
if (component != null) {
gc = component.getGraphicsConfiguration();
}
if (gc == null) {
try {
gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration();
} catch (HeadlessException e) {
logger.warn("No GraphicsConfiguration found, using default size");
return new Dimension(800, 600);
}
}
Rectangle bounds = gc.getBounds();
return new Dimension(bounds.width, bounds.height);
}

/**
Expand Down Expand Up @@ -572,9 +653,7 @@ public static Image constrainImageSize(Image image, ImageObserver observer, int
public static JLabel createSplashComponent(TreeMap<Integer, String> multiResImageMap, Component parent) {
// Use the current monitor so we don't "overflow" computers whose primary
// displays aren't as large as their secondary displays.
Dimension scaledMonitorSize = getScaledScreenSize(parent.getGraphicsConfiguration()
.getDevice()
.getDisplayMode());
Dimension scaledMonitorSize = getScaledScreenSize(parent);
Image imgSplash = parent.getToolkit().getImage(multiResImageMap.floorEntry(scaledMonitorSize.width).getValue());

// wait for splash image to load completely
Expand All @@ -601,9 +680,7 @@ public static JLabel createSplashComponent(TreeMap<Integer, String> multiResImag
public static JLabel createSplashComponent(String imgSplashFile, Component parent) {
// Use the current monitor so we don't "overflow" computers whose primary
// displays aren't as large as their secondary displays.
Dimension scaledMonitorSize = getScaledScreenSize(parent.getGraphicsConfiguration()
.getDevice()
.getDisplayMode());
Dimension scaledMonitorSize = getScaledScreenSize(parent);

Image imgSplash = parent.getToolkit().getImage(imgSplashFile);

Expand Down
12 changes: 11 additions & 1 deletion megamek/src/megamek/common/internationalization/I18n.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import megamek.MegaMek;

Expand All @@ -33,7 +34,7 @@ public class I18n {

private final String defaultBundle;
private final ConcurrentHashMap<String, ResourceBundle> resourceBundles = new ConcurrentHashMap<>();
protected static I18n instance;
protected static volatile I18n instance;

static {
instance = new I18n("megamek.common.messages");
Expand Down Expand Up @@ -80,6 +81,15 @@ public static String getTextAt(String bundleName, String key) {
return "!" + key + "!";
}

/**
* Get the keys of all localized strings in a specific bundle
* @param bundleName the name of the bundle
* @return the list of keys with localization
*/
public static Set<String> getKeys(String bundleName) {
return I18n.getInstance().getResourceBundle(bundleName).keySet();
}

/**
* Get a localized string from the default bundle
* @param key the key of the string
Expand Down
Loading