Skip to content

Commit

Permalink
Improve debug windows (#3375)
Browse files Browse the repository at this point in the history
* improve btimap tracking

* add capitalizeFirstLetters and isRomanNumeral

* fix window resizing

* add FlxGraphicSource.resolveBitmapData

* improve pointer selection

* prevent debug cursors from blocking clicks

* improve debug cursor hotspot

* centralize debug icons

* add shortcuts for tools and windows

* handle FlxColors in watch window

* add scroll bars to watch/tracker

* add Tools to TrackObject and LogBitmap
  • Loading branch information
Geokureli authored Feb 21, 2025
1 parent fa00ba4 commit ec746de
Show file tree
Hide file tree
Showing 10 changed files with 608 additions and 86 deletions.
233 changes: 233 additions & 0 deletions flixel/system/debug/ScrollSprite.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package flixel.system.debug;

import flixel.FlxG;
import openfl.display.DisplayObject;
import openfl.display.Sprite;
import openfl.events.Event;
import openfl.events.MouseEvent;
import openfl.geom.Point;
import openfl.geom.Rectangle;

class ScrollSprite extends Sprite
{
public var maxScrollY(get, never):Float;
inline function get_maxScrollY():Float return this.height - scroll.height;

public var viewHeight(get, never):Float;
inline function get_viewHeight():Float return scroll.height;

/**
* The current amount of scrolling
*/
public var scrollY(get, set):Float;
inline function get_scrollY():Float return scroll.y;
inline function set_scrollY(value):Float
{
scroll.y = value;
updateScroll();
return scroll.y;
}

var scroll = new Rectangle();
var scrollBar:ScrollBar = null;

public function new ()
{
super();

addEventListener(Event.ADDED_TO_STAGE, function (e)
{
final stage = this.stage;
stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseScroll);
addEventListener(Event.REMOVED_FROM_STAGE, (_)->stage.removeEventListener(MouseEvent.MOUSE_WHEEL, onMouseScroll));
});
}

public function createScrollBar()
{
return scrollBar = new ScrollBar(this);
}

function onMouseScroll(e:MouseEvent)
{
if (mouseX > 0 && mouseX < scroll.width && mouseY - scroll.y > 0 && mouseY - scroll.y < scroll.height)
{
scroll.y -= e.delta;
updateScroll();
}
}

public function setScrollSize(width:Float, height:Float)
{
scroll.width = width;
scroll.height = height;

updateScroll();
}

function updateScroll()
{
scrollRect = null;

if (scroll.bottom > this.height)
scroll.y = height - scroll.height;

if (scroll.y < 0)
scroll.y = 0;

scrollRect = scroll;

if (scrollBar != null)
scrollBar.onViewChange();
}

override function addChild(child)
{
super.addChild(child);
updateScroll();
return child;
}

public function isChildVisible(child:DisplayObject)
{
if (getChildIndex(child) == -1)
throw "Invalid child, not a child of this container";

return child.y < scroll.bottom && child.y + child.height > scroll.y;
}
}

@:allow(flixel.system.debug.ScrollSprite)
class ScrollBar extends Sprite
{
static inline final WIDTH = 10;

final target:ScrollSprite;

final handle = new Sprite();
final bg = new Sprite();

var state:ScrollState = IDLE;

public function new (target:ScrollSprite)
{
this.target = target;
super();

bg.mouseChildren = true;
bg.mouseEnabled = true;
bg.graphics.beginFill(0xFFFFFF, 0.1);
bg.graphics.drawRect(0, 0, WIDTH, 1);
bg.graphics.endFill();
addChild(bg);

handle.mouseChildren = true;
handle.mouseEnabled = true;
handle.buttonMode = true;
handle.graphics.beginFill(0xFFFFFF, 0.3);
handle.graphics.drawRect(0, 0, WIDTH, 1);
handle.graphics.endFill();
addChild(handle);

function onAdded(_)
{
removeEventListener(Event.ADDED_TO_STAGE, onAdded);
final stage = this.stage;

bg.addEventListener(MouseEvent.MOUSE_DOWN, onBgMouseDown);
handle.addEventListener(MouseEvent.MOUSE_DOWN, onHandleMouse);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onHandleMouse);

function onRemoved(_)
{
removeEventListener(Event.REMOVED_FROM_STAGE, onRemoved);

bg.removeEventListener(MouseEvent.MOUSE_DOWN, onBgMouseDown);
handle.removeEventListener(MouseEvent.MOUSE_DOWN, onHandleMouse);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, onHandleMouse);
}
addEventListener(Event.REMOVED_FROM_STAGE, onRemoved);
}
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}

function onBgMouseDown(e:MouseEvent)
{
if (state != IDLE)
throw "expected state: IDLE";

state = DRAG_BG;
mouseMoveHelper(e.stageY);
}

function onHandleMouse(e:MouseEvent)
{
if (e.type == MouseEvent.MOUSE_DOWN)
{
if (state != IDLE)
throw "expected state: IDLE";

state = DRAG_HANDLE(getLocalY(e.stageY) - handle.y);
}
else
state = IDLE;
}

function onMouseMove(e:MouseEvent)
{
mouseMoveHelper(e.stageY);
}

function getLocalY(stageY:Float)
{
return globalToLocal(new Point (0, stageY)).y;
}

function mouseMoveHelper(stageY:Float)
{
final localY = getLocalY(stageY);
switch state
{
case IDLE:
case DRAG_HANDLE(offsetY):
handle.y = localY - offsetY;
onHandleMove();
case DRAG_BG:
handle.y = localY - handle.height / 2;
onHandleMove();
}
}

function onHandleMove()
{
if (handle.y < 0)
handle.y = 0;

if (handle.y > bg.height - handle.height)
handle.y = bg.height - handle.height;

target.scrollY = handle.y / (bg.height - handle.height) * target.maxScrollY;
}

public function resize(height:Float)
{
bg.height = height;
handle.height = height / target.height * target.viewHeight;
onViewChange();
}

function onViewChange()
{
mouseEnabled = mouseChildren = visible = target.maxScrollY > 0 && target.maxScrollY < target.height;
handle.y = (target.scrollY / target.maxScrollY) * (bg.height - handle.height);
}
}

private enum ScrollState
{
IDLE;
DRAG_HANDLE(offsetY:Float);
DRAG_BG;
}
4 changes: 4 additions & 0 deletions flixel/system/debug/interaction/Interaction.hx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ import flixel.math.FlxRect;
import flixel.system.debug.Window;
import flixel.system.debug.interaction.tools.Transform;
import flixel.system.debug.interaction.tools.Eraser;
import flixel.system.debug.interaction.tools.LogBitmap;
import flixel.system.debug.interaction.tools.Mover;
import flixel.system.debug.interaction.tools.Pointer;
import flixel.system.debug.interaction.tools.Tool;
import flixel.system.debug.interaction.tools.ToggleBounds;
import flixel.system.debug.interaction.tools.TrackObject;
import flixel.util.FlxDestroyUtil;
import flixel.util.FlxSpriteUtil;
#if !(FLX_NATIVE_CURSOR && FLX_MOUSE)
Expand Down Expand Up @@ -93,6 +95,8 @@ class Interaction extends Window
addTool(new Eraser());
addTool(new Transform());
addTool(new ToggleBounds());
addTool(new LogBitmap());
addTool(new TrackObject());

FlxG.signals.postDraw.add(postDraw);
FlxG.debugger.visibilityChanged.add(handleDebuggerVisibilityChanged);
Expand Down
58 changes: 58 additions & 0 deletions flixel/system/debug/interaction/tools/LogBitmap.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package flixel.system.debug.interaction.tools;

import flixel.FlxG;
import flixel.FlxG;
import openfl.display.Graphics;
import openfl.display.BitmapData;
import flixel.system.debug.interaction.Interaction;
// import flixel.system.debug.Tooltip;

using flixel.util.FlxArrayUtil;


/**
* A tool to add selected sprites to the BitmapLog window
*
* @author George
*/
class LogBitmap extends Tool
{
override function init(brain:Interaction):Tool
{
super.init(brain);

_name = "Log selected bitmaps";
setButton(Icon.bitmapLog);
button.toggleMode = false;

// _tooltip = Tooltip.add(null, "");
// _tooltip.textField.wordWrap = false;

return this;
}

override function update():Void
{
button.enabled = _brain.selectedItems.countLiving() > 0;
button.mouseEnabled = button.enabled;
button.alpha = button.enabled ? 0.3 : 0.1;
}

override function onButtonClicked()
{
#if FLX_DEBUG // needed for coverage tests
// super.onButtonClicked();
if (_brain.selectedItems.length == 0)
return;

for (member in _brain.selectedItems)
{
if (member != null && member is FlxSprite)
{
final sprite:FlxSprite = cast member;
FlxG.bitmapLog.add(sprite.graphic);
}
}
#end
}
}
48 changes: 48 additions & 0 deletions flixel/system/debug/interaction/tools/TrackObject.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package flixel.system.debug.interaction.tools;

import flixel.FlxG;
import flixel.math.FlxRect;
import flixel.system.debug.interaction.Interaction;
import openfl.display.Graphics;
import openfl.display.BitmapData;

using flixel.util.FlxArrayUtil;

/**
* A tool to open a tracker for the selected object
*
* @author George
*/
class TrackObject extends Tool
{
override function init(brain:Interaction):Tool
{
super.init(brain);

_name = "Track object";
setButton(Icon.watch);
button.toggleMode = true;

setCursor(Icon.watch, -5, -5);

return this;
}

#if FLX_DEBUG
override function update():Void
{
if (isActive() && _brain.pointerJustPressed)
{
final rect = FlxRect.get(_brain.flixelPointer.x, _brain.flixelPointer.y, 1, 1);
final item = _brain.getTopItemWithinState(FlxG.state, rect);
if (item != null)
{
FlxG.debugger.track(item);
_brain.selectedItems.clear();
_brain.selectedItems.add(item);
}
rect.put();
}
}
#end
}
6 changes: 5 additions & 1 deletion flixel/system/debug/watch/EditableTextField.hx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package flixel.system.debug.watch;

import flixel.math.FlxMath;
import flixel.util.FlxDestroyUtil.IFlxDestroyable;
import flixel.util.FlxDestroyUtil;
import flixel.util.FlxColor;
import openfl.events.FocusEvent;
import openfl.events.KeyboardEvent;
import openfl.events.MouseEvent;
Expand All @@ -11,6 +12,8 @@ import openfl.text.TextFormat;
import openfl.ui.Keyboard;
import Type.ValueType;

using StringTools;

class EditableTextField extends TextField implements IFlxDestroyable
{
public var isEditing(default, null):Bool;
Expand Down Expand Up @@ -142,6 +145,7 @@ class EditableTextField extends TextField implements IFlxDestroyable
{
var value:Dynamic = switch (expectedType)
{
case TInt if (text.startsWith("#") || text.startsWith("0x")): FlxColor.fromString(text);
case TInt: Std.parseInt(text);
case TFloat: Std.parseFloat(text);
case TBool if (text == "true"): true;
Expand Down
Loading

0 comments on commit ec746de

Please sign in to comment.