[libbluray-devel] [Git][videolan/libbluray][master] BD-J: HAVI navigable component implementations
Petri Hintukainen (@hpi)
gitlab at videolan.org
Mon Jun 8 16:50:30 UTC 2026
Petri Hintukainen pushed to branch master at VideoLAN / libbluray
Commits:
4adb2273 by spotter at 2026-06-08T16:50:21+00:00
BD-J: HAVI navigable component implementations
- - - - -
4 changed files:
- src/libbluray/bdj/java/org/havi/ui/HComponent.java
- src/libbluray/bdj/java/org/havi/ui/HIcon.java
- + src/libbluray/bdj/java/org/havi/ui/HNavigableHelper.java
- src/libbluray/bdj/java/org/havi/ui/HText.java
Changes:
=====================================
src/libbluray/bdj/java/org/havi/ui/HComponent.java
=====================================
@@ -21,7 +21,10 @@
package org.havi.ui;
import java.awt.Component;
+import java.awt.event.FocusEvent;
+
import org.dvb.ui.TestOpacity;
+import org.havi.ui.event.HFocusEvent;
import java.awt.BDToolkit;
@@ -63,6 +66,18 @@ public abstract class HComponent extends Component implements HMatteLayer, TestO
return super.isEnabled();
}
+ /**
+ * Override AWT focus processing to create HFocusEvent and delegate to processHFocusEvent
+ * for all HNavigationInputPreferred implementations.
+ */
+ protected void processFocusEvent(FocusEvent e) {
+ if (this instanceof HNavigationInputPreferred) {
+ HFocusEvent hEvent = new HFocusEvent(this, e.getID());
+ ((HNavigationInputPreferred) this).processHFocusEvent(hEvent);
+ }
+ super.processFocusEvent(e);
+ }
+
private HMatte matte = null;
private static final long serialVersionUID = -4115249517434074428L;
=====================================
src/libbluray/bdj/java/org/havi/ui/HIcon.java
=====================================
@@ -1,6 +1,7 @@
/*
* This file is part of libbluray
* Copyright (C) 2010 William Hahne
+ * Copyright (C) 2026 libbluray project
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -20,29 +21,42 @@
package org.havi.ui;
import java.awt.Image;
+import java.awt.event.KeyEvent;
import org.havi.ui.event.HFocusEvent;
import org.havi.ui.event.HFocusListener;
import org.videolan.BDJXletContext;
-import org.videolan.Logger;
public class HIcon extends HStaticIcon implements HNavigable {
+
+ private HNavigableHelper helper;
+
public HIcon() {
- org.videolan.Logger.unimplemented(HIcon.class.getName(), "");
+ super();
+ init();
}
public HIcon(Image image) {
- org.videolan.Logger.unimplemented(HIcon.class.getName(), "");
+ super(image);
+ init();
}
public HIcon(Image image, int x, int y, int width, int height) {
- org.videolan.Logger.unimplemented(HIcon.class.getName(), "");
+ super(image, x, y, width, height);
+ init();
+ }
+
+ public HIcon(Image imageNormal, Image imageFocus, int x, int y, int width, int height) {
+ super(imageNormal, x, y, width, height);
+ setGraphicContent(imageFocus, FOCUSED_STATE);
+ init();
}
- public HIcon(Image imageNormal, Image imageFocus, int x, int y, int width,
- int height) {
- org.videolan.Logger.unimplemented(HIcon.class.getName(), "");
+ private void init() {
+ helper = new HNavigableHelper(this);
+ // Enable key and focus events so processKeyEvent/processFocusEvent are called
+ enableEvents(java.awt.AWTEvent.KEY_EVENT_MASK | java.awt.AWTEvent.FOCUS_EVENT_MASK);
}
public static void setDefaultLook(HGraphicLook hlook) {
@@ -53,58 +67,79 @@ public class HIcon extends HStaticIcon implements HNavigable {
return (HGraphicLook)BDJXletContext.getXletDefaultLook(PROPERTY_LOOK, DEFAULT_LOOK);
}
+ // --- HNavigable implementation ---
+
public void setMove(int keyCode, HNavigable target) {
- Logger.unimplemented("", "");
+ helper.setMove(keyCode, target);
}
public HNavigable getMove(int keyCode) {
- Logger.unimplemented("", "");
- return null;
+ return helper.getMove(keyCode);
}
public void setFocusTraversal(HNavigable up, HNavigable down,
HNavigable left, HNavigable right) {
- Logger.unimplemented("", "");
+ helper.setFocusTraversal(up, down, left, right);
}
public boolean isSelected() {
- Logger.unimplemented("", "");
- return false;
+ return helper.isSelected();
}
public void setGainFocusSound(HSound sound) {
- Logger.unimplemented("", "");
+ helper.setGainFocusSound(sound);
}
public void setLoseFocusSound(HSound sound) {
- Logger.unimplemented("", "");
+ helper.setLoseFocusSound(sound);
}
public HSound getGainFocusSound() {
- Logger.unimplemented("", "");
- return null;
+ return helper.getGainFocusSound();
}
public HSound getLoseFocusSound() {
- Logger.unimplemented("", "");
- return null;
+ return helper.getLoseFocusSound();
}
public void addHFocusListener(HFocusListener l) {
- Logger.unimplemented("", "");
+ helper.addHFocusListener(l);
}
public void removeHFocusListener(HFocusListener l) {
- Logger.unimplemented("", "");
+ helper.removeHFocusListener(l);
}
public int[] getNavigationKeys() {
- Logger.unimplemented("", "");
- return null;
+ return helper.getNavigationKeys();
}
+ // --- Focus event handling ---
+
+ /**
+ * Process HAVI focus events. Updates interaction state and notifies listeners.
+ */
public void processHFocusEvent(HFocusEvent evt) {
- Logger.unimplemented("", "");
+ int state = getInteractionState();
+ int newState = helper.processHFocusEvent(evt);
+
+ if (state != newState) {
+ setInteractionState(newState);
+ }
+ }
+
+ /**
+ * Process key events for HAVI navigation.
+ * Delegates to HNavigableHelper for arrow key handling.
+ */
+ protected void processKeyEvent(KeyEvent e) {
+ // Let helper handle navigation keys
+ if (helper.processKeyEvent(e)) {
+ e.consume();
+ return;
+ }
+
+ super.processKeyEvent(e);
}
static final Class DEFAULT_LOOK = HGraphicLook.class;
=====================================
src/libbluray/bdj/java/org/havi/ui/HNavigableHelper.java
=====================================
@@ -0,0 +1,209 @@
+/*
+ * This file is part of libbluray
+ * Copyright (C) 2024 libbluray project
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Based on XletView implementation by Martin Sveden.
+ */
+
+package org.havi.ui;
+
+import java.awt.Component;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
+import java.util.Hashtable;
+
+import org.havi.ui.event.HFocusEvent;
+import org.havi.ui.event.HFocusListener;
+
+/**
+ * Helper class that implements HNavigable functionality.
+ * This encapsulates the navigation logic for use by HIcon, HGraphicButton, etc.
+ */
+public final class HNavigableHelper {
+
+ private Hashtable navTargets = new Hashtable();
+ private HSound gainFocusSound;
+ private HSound loseFocusSound;
+ transient HFocusListener hFocusListener;
+
+ private HVisible hVisible;
+
+ public HNavigableHelper(HVisible hVisible) {
+ this.hVisible = hVisible;
+ }
+
+ /**
+ * Sets a navigation target for the specified key code.
+ * @param keyCode The key code (e.g., KeyEvent.VK_UP)
+ * @param target The HNavigable to transfer focus to, or null to remove
+ */
+ public void setMove(int keyCode, HNavigable target) {
+ Integer code = Integer.valueOf(keyCode);
+ navTargets.remove(code);
+ if (target != null) {
+ navTargets.put(code, target);
+ }
+ }
+
+ /**
+ * Gets the navigation target for the specified key code.
+ * @param keyCode The key code
+ * @return The HNavigable target, or null if not set
+ */
+ public HNavigable getMove(int keyCode) {
+ return (HNavigable) navTargets.get(Integer.valueOf(keyCode));
+ }
+
+ /**
+ * Sets navigation targets for all four arrow keys at once.
+ */
+ public void setFocusTraversal(HNavigable up, HNavigable down, HNavigable left, HNavigable right) {
+ setMove(KeyEvent.VK_UP, up);
+ setMove(KeyEvent.VK_DOWN, down);
+ setMove(KeyEvent.VK_LEFT, left);
+ setMove(KeyEvent.VK_RIGHT, right);
+ }
+
+ /**
+ * Returns true if this component currently has focus.
+ */
+ public boolean isSelected() {
+ return hVisible.hasFocus();
+ }
+
+ public void setGainFocusSound(HSound sound) {
+ gainFocusSound = sound;
+ }
+
+ public void setLoseFocusSound(HSound sound) {
+ loseFocusSound = sound;
+ }
+
+ public HSound getGainFocusSound() {
+ return gainFocusSound;
+ }
+
+ public HSound getLoseFocusSound() {
+ return loseFocusSound;
+ }
+
+ /**
+ * Adds an HFocusListener to receive focus events.
+ */
+ public synchronized void addHFocusListener(HFocusListener listener) {
+ hFocusListener = HEventMulticaster.add(hFocusListener, listener);
+ }
+
+ /**
+ * Removes an HFocusListener.
+ */
+ public synchronized void removeHFocusListener(HFocusListener listener) {
+ hFocusListener = HEventMulticaster.remove(hFocusListener, listener);
+ }
+
+ /**
+ * Returns an array of key codes for which navigation targets are set.
+ */
+ public int[] getNavigationKeys() {
+ if (navTargets.size() == 0) {
+ return null;
+ }
+ int[] keyCodes = new int[navTargets.size()];
+ java.util.Enumeration keys = navTargets.keys();
+ int i = 0;
+ while (keys.hasMoreElements()) {
+ Integer key = (Integer) keys.nextElement();
+ keyCodes[i++] = key.intValue();
+ }
+ return keyCodes;
+ }
+
+ /**
+ * Processes an HFocusEvent and returns the new interaction state.
+ * This is the core method that handles focus gain/loss and focus transfers.
+ *
+ * @param evt The HFocusEvent to process
+ * @return The new interaction state for the HVisible
+ */
+ public int processHFocusEvent(HFocusEvent evt) {
+ int state = hVisible.getInteractionState();
+
+ if (evt.getID() == FocusEvent.FOCUS_GAINED) {
+ // Set focused bit
+ state = state | HState.FOCUSED_STATE_BIT;
+
+ // Play gain focus sound
+ if (gainFocusSound != null) {
+ gainFocusSound.play();
+ }
+
+ // Notify HFocusListeners
+ if (hFocusListener != null) {
+ hFocusListener.focusGained(evt);
+ }
+ }
+ else if (evt.getID() == FocusEvent.FOCUS_LOST) {
+ // Clear focused bit (XOR to toggle off)
+ state = state & (~HState.FOCUSED_STATE_BIT);
+
+ // Play lose focus sound
+ if (loseFocusSound != null) {
+ loseFocusSound.play();
+ }
+
+ // Notify HFocusListeners
+ if (hFocusListener != null) {
+ hFocusListener.focusLost(evt);
+ }
+ }
+ else if (evt.getID() == HFocusEvent.FOCUS_TRANSFER &&
+ evt.getTransferId() != HFocusEvent.NO_TRANSFER_ID) {
+ // Handle focus transfer to navigation target
+ HNavigable newNav = getMove(evt.getTransferId());
+
+ if (newNav instanceof Component) {
+ ((Component) newNav).requestFocus();
+ }
+ }
+
+ return state;
+ }
+
+ /**
+ * Processes a KeyEvent for navigation.
+ * Looks up navigation targets and transfers focus.
+ *
+ * @param e The KeyEvent to process
+ * @return true if the event was handled, false otherwise
+ */
+ public boolean processKeyEvent(KeyEvent e) {
+ // Only handle KEY_PRESSED
+ if (e.getID() != KeyEvent.KEY_PRESSED) {
+ return false;
+ }
+
+ // Look up navigation target for this key
+ HNavigable target = getMove(e.getKeyCode());
+
+ if (target != null && target instanceof Component) {
+ ((Component) target).requestFocus();
+ return true;
+ }
+
+ return false;
+ }
+}
=====================================
src/libbluray/bdj/java/org/havi/ui/HText.java
=====================================
@@ -2,6 +2,7 @@
* This file is part of libbluray
* Copyright (C) 2010 William Hahne
* Copyright (C) 2013 Petri Hintukainen <phintuka at users.sourceforge.net>
+ * Copyright (C) 2026 libbluray project
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -22,6 +23,7 @@ package org.havi.ui;
import java.awt.Color;
import java.awt.Font;
+import java.awt.event.KeyEvent;
import org.havi.ui.event.HFocusEvent;
import org.havi.ui.event.HFocusListener;
@@ -30,6 +32,9 @@ import org.videolan.BDJXletContext;
import org.videolan.Logger;
public class HText extends HStaticText implements HNavigable {
+
+ private HNavigableHelper helper;
+
public HText() {
this(null);
}
@@ -61,6 +66,8 @@ public class HText extends HStaticText implements HNavigable {
super.setTextContent(textFocus, DISABLED_FOCUSED_STATE);
super.setTextContent(textFocus, DISABLED_ACTIONED_FOCUSED_STATE);
}
+
+ init();
}
public HText(String text, Font font, Color foreground, Color background,
@@ -88,62 +95,80 @@ public class HText extends HStaticText implements HNavigable {
setTextLayoutManager(tlm);
}
- public void setMove(int keyCode, HNavigable target) {
- logger.unimplemented("setMove");
+ private void init() {
+ helper = new HNavigableHelper(this);
+ // Enable key and focus events so processKeyEvent/processFocusEvent are called
+ enableEvents(java.awt.AWTEvent.KEY_EVENT_MASK | java.awt.AWTEvent.FOCUS_EVENT_MASK);
}
- public HNavigable getMove(int keyCode) {
- logger.unimplemented("getMove");
- return this;
+ // --- HNavigable implementation ---
+
+ public void setMove(int keyCode, HNavigable target) {
+ helper.setMove(keyCode, target);
}
- public boolean isFocusable() {
- return true;
+ public HNavigable getMove(int keyCode) {
+ return helper.getMove(keyCode);
}
public void setFocusTraversal(HNavigable up, HNavigable down,
HNavigable left, HNavigable right) {
- logger.unimplemented("setFocusTraversal");
+ helper.setFocusTraversal(up, down, left, right);
}
public boolean isSelected() {
- logger.unimplemented("isSelected");
- return false;
+ return helper.isSelected();
}
public void setGainFocusSound(HSound sound) {
- logger.unimplemented("setGainFocusSound");
+ helper.setGainFocusSound(sound);
}
public void setLoseFocusSound(HSound sound) {
- logger.unimplemented("setLoseFocusSound");
+ helper.setLoseFocusSound(sound);
}
public HSound getGainFocusSound() {
- logger.unimplemented("getGainFocusSound");
- return null;
+ return helper.getGainFocusSound();
}
public HSound getLoseFocusSound() {
- logger.unimplemented("getLoseFocusSound");
- return null;
+ return helper.getLoseFocusSound();
}
public void addHFocusListener(HFocusListener l) {
- logger.unimplemented("addHFocusListener");
+ helper.addHFocusListener(l);
}
public void removeHFocusListener(HFocusListener l) {
- logger.unimplemented("removeHFocusListener");
+ helper.removeHFocusListener(l);
}
public int[] getNavigationKeys() {
- logger.unimplemented("getNavigationKeys");
- return null;
+ return helper.getNavigationKeys();
}
+ // --- Focus event handling ---
+
public void processHFocusEvent(HFocusEvent evt) {
- logger.unimplemented("processHFocusEvent");
+ int state = getInteractionState();
+ int newState = helper.processHFocusEvent(evt);
+
+ if (state != newState) {
+ setInteractionState(newState);
+ }
+ }
+
+ /**
+ * Process key events for HAVI navigation.
+ * Delegates to HNavigableHelper for arrow key handling.
+ */
+ protected void processKeyEvent(KeyEvent e) {
+ if (helper.processKeyEvent(e)) {
+ e.consume();
+ return;
+ }
+ super.processKeyEvent(e);
}
public static void setDefaultLook(HTextLook hlook) {
View it on GitLab: https://code.videolan.org/videolan/libbluray/-/commit/4adb2273a72e0cee7d462f3d7df9e1c106159176
--
View it on GitLab: https://code.videolan.org/videolan/libbluray/-/commit/4adb2273a72e0cee7d462f3d7df9e1c106159176
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the libbluray-devel
mailing list