Skip to content

feat: xbox control support#2922

Draft
tangge233 wants to merge 8 commits into
devfrom
feat/xbox
Draft

feat: xbox control support#2922
tangge233 wants to merge 8 commits into
devfrom
feat/xbox

Conversation

@tangge233
Copy link
Copy Markdown
Contributor

@tangge233 tangge233 commented May 30, 2026

蹭个编译产物的,暂时不合并

Sourcery 总结

在主窗口以及常用控件中添加初步的 Xbox 手柄导航和激活支持。

新功能:

  • 引入一个手柄模块,用于轮询 Xbox 控制器,将按键映射到 UI 导航和操作,并在需要时模拟键盘/鼠标输入。
  • 添加一个可视化高亮覆盖层,在通过手柄导航时指示当前获得焦点的控件。
  • 通过手柄按键启用标题栏和对话框交互,包括标签页切换和窗口控制操作。

增强:

  • 将常见的自定义控件标记为可选中和可激活,使其能够参与键盘/手柄的焦点导航和激活。
  • 为列表和卡片控件扩展编程式点击和切换方法,以供手柄激活动作逻辑使用。
Original summary in English

Summary by Sourcery

Add initial Xbox gamepad navigation and activation support across the main window and common controls.

New Features:

  • Introduce a gamepad module that polls an Xbox controller, maps buttons to UI navigation and actions, and simulates keyboard/mouse input as needed.
  • Add a visual highlight overlay to indicate the currently focused control when navigating via gamepad.
  • Enable title bar and dialog interactions via gamepad buttons, including tab switching and window control actions.

Enhancements:

  • Mark common custom controls as selectable and activatable to participate in keyboard/gamepad focus navigation and activation.
  • Extend list and card controls with programmatic click and swap toggle methods used by gamepad activation logic.

tangge233 added 8 commits May 25, 2026 18:49
…tItem focus

- Replace _lastPageLeft/Right ReferenceEquals polling with DP hook on PanMainLeft/Right.Child
- Replace _pendingFocusTargetLeft toggle with IsFocusOnLeftPage() visual tree walk
- Set Focusable=true in SetCanSelect so MyListItem (Grid) and other controls accept focus
- Reject non-selectable destinations in MoveFocus to prevent highlight loss
- Invert scroll direction (thumb up = scroll up)
- CheckPageChanged now tries both pages
- Save/restore focus on title bar mode enter/exit
…HighlightFromFocus

- Extract HideHighlight() to reduce redundant null checks
- Extract GetForm() to avoid repeating ModMain.FrmMain
- Split GetTitleBarButton(int) into GetTitleBarButton() + MoveTitleBarIndex(int)
- Fix double UpdateHighlightFromFocus() in title bar input path
- Simplify CheckPageChanged to avoid array allocation
- Store _pageChildProp for DP handler cleanup on Shutdown
- Set _pendingPageFocus = true at declaration
- Remove duplicate OnPageChildChanged method
- Add PerformClick() to MyListItem, extracting click logic from
  Button_MouseUp without IsMouseDirectlyOver dependency
- Add MyListItem special case in KeyControllAbility.Activate()
- A button now works on MyListItem (Clickable/RadioBox/CheckBox)
ModGamepad.cs        (144 lines) — fields, constants, public API, polling loop
ModGamepadHighlight.cs (178 lines) — overlay creation, highlight positioning, event handlers
ModGamepadInput.cs   (196 lines) — button dispatch, title bar mode, simulate, dialog
ModGamepadNavigation.cs (108 lines) — focus navigation, page tracking, scope stack
@pcl-ce-automation pcl-ce-automation Bot added 🚧 正在处理 开发人员正在对该内容进行开发、测试或修复,进展中 size: XL PR 大小评估:超大型 labels May 30, 2026
@tangge233 tangge233 changed the title feat: xbox controll support feat: xbox control support May 30, 2026
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 30, 2026

Reviewer's Guide

通过引入通用的键盘/手柄焦点/激活辅助工具,接入 FormMain 的手柄生命周期,实现基于控制器的导航、激活、滚动、标题栏控制、对话框交互,以及在现有自定义控件上的可视高亮,从而新增对 Xbox 控制器的支持。

手柄 A 键激活流程的时序图

sequenceDiagram
    participant Controller
    participant ModGamepad
    participant KeyControllAbility
    participant Control as FocusedControl
    participant MyListItem
    participant MyCard

    Controller->>ModGamepad: GetState()
    ModGamepad->>ModGamepad: HandleButtonPress(GamepadButtonFlags.A)
    ModGamepad->>KeyControllAbility: Activate(FocusedControl)
    alt FocusedControl is MyCard with SwapControl
        KeyControllAbility->>MyCard: ToggleSwap()
    else FocusedControl is MyListItem
        KeyControllAbility->>MyListItem: PerformClick()
    else Other control
        KeyControllAbility->>Control: RaiseEvent(MouseLeftButtonDownEvent)
        KeyControllAbility->>Control: RaiseEvent(MouseLeftButtonUpEvent)
    end
    ModGamepad->>ModGamepad: UpdateHighlightFromFocus()
Loading

File-Level Changes

Change Details Files
引入可复用的控件焦点/激活能力辅助工具,并将其接入大部分交互式 UI 元素,使其能够参与键盘/手柄导航与激活。
  • 添加 KeyControllAbility 附加属性(CanSelect/CanActivate),以及用于在子树中查找/选择第一个可选元素、以编程方式激活控件的辅助方法,其中对 MyCard 和 MyListItem 提供自定义处理。
  • 更新关键自定义控件(按钮、列表项、滑块、文本框、菜单项等)的构造函数,以按需启用 KeyControllAbility 的选择和/或激活能力,确保在需要时它们可获取焦点。
Plain Craft Launcher 2/Controls/KeyControllAbility.cs
Plain Craft Launcher 2/Controls/MyListItem.xaml.cs
Plain Craft Launcher 2/Controls/MyCard.cs
Plain Craft Launcher 2/Controls/MyButton.xaml.cs
Plain Craft Launcher 2/Controls/MyCheckBox.xaml.cs
Plain Craft Launcher 2/Controls/MyComboBox.cs
Plain Craft Launcher 2/Controls/MyComboBoxItem.cs
Plain Craft Launcher 2/Controls/MyExtraButton.xaml.cs
Plain Craft Launcher 2/Controls/MyExtraTextButton.xaml.cs
Plain Craft Launcher 2/Controls/MyHint.xaml.cs
Plain Craft Launcher 2/Controls/MyIconButton.xaml.cs
Plain Craft Launcher 2/Controls/MyIconTextButton.xaml.cs
Plain Craft Launcher 2/Controls/MyLoading.xaml.cs
Plain Craft Launcher 2/Controls/MyMenuItem.cs
Plain Craft Launcher 2/Controls/MyRadioBox.xaml.cs
Plain Craft Launcher 2/Controls/MyRadioButton.xaml.cs
Plain Craft Launcher 2/Controls/MySlider.xaml.cs
Plain Craft Launcher 2/Controls/MyTextBox.cs
Plain Craft Launcher 2/Controls/MyTextButton.cs
扩展现有列表和卡片控件,加入可编程的点击/切换(swap)API,使其能够通过手柄激活逻辑触发,而不仅仅依赖鼠标事件。
  • 新增 MyListItem.PerformClick,用于集中处理点击逻辑,包括日志记录、单选/复选框状态变化以及自定义事件触发,并在手柄激活辅助逻辑中复用。
  • 新增 MyCard.ToggleSwap,用于封装切换操作(触发自定义事件和预览事件,并尊重 handled 状态),并暴露给激活辅助逻辑使用。
Plain Craft Launcher 2/Controls/MyListItem.xaml.cs
Plain Craft Launcher 2/Controls/MyCard.cs
引入手柄模块,用于轮询 Xbox 控制器,将输入转换为导航/激活动作,模拟滚动/键盘事件,并与主窗体生命周期集成。
  • 创建 ModGamepad.Initialize/Shutdown,用于设置 XInput Controller、启动后台轮询线程、挂接页面切换处理程序,并在关闭时完成清理。
  • 实现一个轮询循环,用于对按键按下进行去抖,支持 DPad 自动连发,处理带累积和死区的右摇杆滚动,并定期刷新高亮/焦点状态。
  • 在 FormMain 中添加生命周期钩子,在窗口加载时初始化手柄子系统,在程序退出时关闭。
Plain Craft Launcher 2/Modules/Gamepad/ModGamepad.cs
Plain Craft Launcher 2/Modules/Gamepad/ModGamepadInput.cs
Plain Craft Launcher 2/Modules/Gamepad/ModGamepadNavigation.cs
Plain Craft Launcher 2/Plain Craft Launcher 2.csproj
Plain Craft Launcher 2/FormMain.xaml.cs
在主界面和各类对话框中提供感知控制器的焦点导航以及页面/标题/标签切换行为。
  • 将 DPad 映射为焦点遍历(在遍历失败时回退到页面级焦点),将肩键映射为标题标签切换,同时用 Start/Back 和 B/Y 管理标题栏模式、页面切换、作用域栈以及 Escape 模拟。
  • 支持模态对话框:通过对各种 MyMsg* 对话框类型的主/次/第三按钮进行直接方法调用,将 A/B/X 映射到相应按钮。
  • 实现基于焦点作用域的导航辅助方法(CheckPageChanged、IsFocusOnLeftPage、TryFocusOnPage、MoveFocus、NavigateTitleTab),它们构建在 KeyControllAbility 和 WPF 焦点 API 之上。
Plain Craft Launcher 2/Modules/Gamepad/ModGamepadInput.cs
Plain Craft Launcher 2/Modules/Gamepad/ModGamepadNavigation.cs
添加一个可视高亮覆盖层,用于跟踪当前由手柄聚焦的控件(以及标题栏按钮),并在鼠标/键盘输入接管时禁用该高亮。
  • 在 PanMain 上方创建一个由 Canvas+Border 组成的覆盖层,在聚焦控件或标题栏按钮周围绘制圆角矩形,并为普通模式和标题栏模式使用不同的画刷。
  • 跟踪鼠标/键盘活动,在手柄激活模式与指针/键盘激活模式之间切换,在合适时隐藏高亮,并在页面切换时重置焦点作用域。
  • 使用相对于 PanForm 的变换来定位目标元素周围的高亮,在变换失败时进行安全回退。
Plain Craft Launcher 2/Modules/Gamepad/ModGamepadHighlight.cs

Tips and commands

Interacting with Sourcery

  • 触发新的代码审查: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 在回复某条审查评论时,要求 Sourcery 从该评论创建 issue。你也可以在审查评论下回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题中任意位置写上 @sourcery-ai,即可随时生成标题。你也可以在 pull request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文任意位置写上 @sourcery-ai summary,即可在对应位置随时生成 PR 摘要。你也可以在 pull request 中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成审查者指南: 在 pull request 中评论 @sourcery-ai guide,即可随时(重新)生成审查者指南。
  • 解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。适用于你已经处理了所有评论且不希望再看到它们的情况。
  • 撤销所有 Sourcery 审查: 在 pull request 中评论 @sourcery-ai dismiss,即可撤销所有现有的 Sourcery 审查。尤其适用于你想从头开始一次新的审查——别忘了再评论 @sourcery-ai review 来触发新审查!

Customizing Your Experience

打开你的 dashboard 以:

  • 启用或禁用审查功能,例如 Sourcery 生成的 pull request 摘要、审查者指南等。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查指令。
  • 调整其他审查设置。

Getting Help

Original review guide in English

Reviewer's Guide

Adds Xbox controller support by introducing a generic keyboard/gamepad focus/activation helper, wiring gamepad lifecycle into FormMain, and implementing controller-driven navigation, activation, scrolling, title bar control, dialog interaction, and visual highlighting across existing custom controls.

Sequence diagram for gamepad A button activation flow

sequenceDiagram
    participant Controller
    participant ModGamepad
    participant KeyControllAbility
    participant Control as FocusedControl
    participant MyListItem
    participant MyCard

    Controller->>ModGamepad: GetState()
    ModGamepad->>ModGamepad: HandleButtonPress(GamepadButtonFlags.A)
    ModGamepad->>KeyControllAbility: Activate(FocusedControl)
    alt FocusedControl is MyCard with SwapControl
        KeyControllAbility->>MyCard: ToggleSwap()
    else FocusedControl is MyListItem
        KeyControllAbility->>MyListItem: PerformClick()
    else Other control
        KeyControllAbility->>Control: RaiseEvent(MouseLeftButtonDownEvent)
        KeyControllAbility->>Control: RaiseEvent(MouseLeftButtonUpEvent)
    end
    ModGamepad->>ModGamepad: UpdateHighlightFromFocus()
Loading

File-Level Changes

Change Details Files
Introduce a reusable focus/activation capability helper for controls and wire it into most interactive UI elements so they can participate in keyboard/gamepad navigation and activation.
  • Add KeyControllAbility attached properties (CanSelect/CanActivate) plus helpers to find/select the first selectable element in a subtree and to programmatically activate a control, including custom handling for MyCard and MyListItem.
  • Update constructors of key custom controls (buttons, list items, sliders, text boxes, menu items, etc.) to enable KeyControllAbility selection and/or activation as appropriate, ensuring they become focusable if needed.
Plain Craft Launcher 2/Controls/KeyControllAbility.cs
Plain Craft Launcher 2/Controls/MyListItem.xaml.cs
Plain Craft Launcher 2/Controls/MyCard.cs
Plain Craft Launcher 2/Controls/MyButton.xaml.cs
Plain Craft Launcher 2/Controls/MyCheckBox.xaml.cs
Plain Craft Launcher 2/Controls/MyComboBox.cs
Plain Craft Launcher 2/Controls/MyComboBoxItem.cs
Plain Craft Launcher 2/Controls/MyExtraButton.xaml.cs
Plain Craft Launcher 2/Controls/MyExtraTextButton.xaml.cs
Plain Craft Launcher 2/Controls/MyHint.xaml.cs
Plain Craft Launcher 2/Controls/MyIconButton.xaml.cs
Plain Craft Launcher 2/Controls/MyIconTextButton.xaml.cs
Plain Craft Launcher 2/Controls/MyLoading.xaml.cs
Plain Craft Launcher 2/Controls/MyMenuItem.cs
Plain Craft Launcher 2/Controls/MyRadioBox.xaml.cs
Plain Craft Launcher 2/Controls/MyRadioButton.xaml.cs
Plain Craft Launcher 2/Controls/MySlider.xaml.cs
Plain Craft Launcher 2/Controls/MyTextBox.cs
Plain Craft Launcher 2/Controls/MyTextButton.cs
Extend existing list and card controls with programmatic click/swap APIs so they can be triggered from gamepad activation logic instead of only mouse events.
  • Add MyListItem.PerformClick to centralize click handling, including logging, radio/checkbox state changes, and custom event raising, and use it from the gamepad activation helper.
  • Add MyCard.ToggleSwap to encapsulate swap toggling (raising custom events and preview events, honoring handled state) and expose it for use by the activation helper.
Plain Craft Launcher 2/Controls/MyListItem.xaml.cs
Plain Craft Launcher 2/Controls/MyCard.cs
Introduce a gamepad module that polls an Xbox controller, translates input into navigation/activation actions, simulates scroll/keyboard events, and integrates with the main form lifecycle.
  • Create ModGamepad.Initialize/Shutdown to set up an XInput Controller, spawn a background polling thread, hook page-change handlers, and tear down on shutdown.
  • Implement a polling loop that debounces button presses, supports DPad auto-repeat, handles right-stick scrolling with accumulation and deadzone, and periodically refreshes highlight/focus state.
  • Add lifecycle hooks in FormMain to initialize the gamepad subsystem on window load and shut it down on program end.
Plain Craft Launcher 2/Modules/Gamepad/ModGamepad.cs
Plain Craft Launcher 2/Modules/Gamepad/ModGamepadInput.cs
Plain Craft Launcher 2/Modules/Gamepad/ModGamepadNavigation.cs
Plain Craft Launcher 2/Plain Craft Launcher 2.csproj
Plain Craft Launcher 2/FormMain.xaml.cs
Provide controller-aware focus navigation and page/title/tab switching behavior across the main UI and dialogs.
  • Map DPad to focus traversal (with fallbacks to page-level focus when traversal fails) and shoulder buttons to title-tab changes, while Start/Back and B/Y manage title-bar mode, page switching, scope stack, and Escape simulation.
  • Support modal dialogs by mapping A/B/X to primary/secondary/tertiary buttons on various MyMsg* dialog types via direct method calls.
  • Implement focus-scoped navigation helpers (CheckPageChanged, IsFocusOnLeftPage, TryFocusOnPage, MoveFocus, NavigateTitleTab) built on top of KeyControllAbility and WPF focus APIs.
Plain Craft Launcher 2/Modules/Gamepad/ModGamepadInput.cs
Plain Craft Launcher 2/Modules/Gamepad/ModGamepadNavigation.cs
Add a visual highlight overlay that tracks the currently gamepad-focused control (and title-bar buttons) and disable it when mouse/keyboard input takes over.
  • Create an overlay Canvas+Border atop PanMain that draws a rounded rectangle around the focused control or title-bar button, with different brushes for normal vs title-bar mode.
  • Track mouse/keyboard activity to switch between gamepad-active and pointer/keyboard-active modes, hiding the highlight when appropriate and resetting focus scope on page changes.
  • Use transforms relative to PanForm to position the highlight around the target element, falling back safely when transforms fail.
Plain Craft Launcher 2/Modules/Gamepad/ModGamepadHighlight.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: XL PR 大小评估:超大型 🚧 正在处理 开发人员正在对该内容进行开发、测试或修复,进展中

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant