port(2V0-a): EterPythonLib 窗口系统的头进镜像

2V0 的入口实测:audit/slices/2V0.json 的 212 个头里 164 个已经在镜像中,缺 48 个
(1 个 EterLib、5 个 EterPythonLib、42 个 UserInterface);10 个单元里已移植 2 个。
本轮补 EterPythonLib 这 5 个,外加必须先有的 EterPythonLib/StdAfx.h —— port_header_gate
按 40250 的做法把每个头放在所属库的 StdAfx.h 后面编译,没有它这五个既看不到
<Python.h> 也看不到 STL 和 CSingleton。

6 个文件全是 port_copy.py 的机械拷贝,0 处手改。

顺带修正清单的一处误报:EterLib/Dimm.h 不是 40250 的源文件,是 Windows SDK <dimm.h>
的 MIDL 产物,port/common/shim/win32/Dimm.h 已经顶掉了;镜像一份反而遮住 shim,
EterLib/IME.h 的 #include "Dimm.h" 会去找 rpc.h。拷进来验证后删掉。

只有头,没有 .cpp,所以没有 port-map 条目 —— 只有声明,没有可调用的东西。
port_header_gate 新增 6 个 TU 全绿;ctest 27/27。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-23 10:50:24 +09:00
co-authored by Claude Opus 5
parent 0a1cdf3d3e
commit 369a80fc10
8 changed files with 1065 additions and 0 deletions
+1
View File
@@ -500,3 +500,4 @@
{"time": "2026-09-23T22:35:00Z", "event": "known_issue", "unit": "Android GBK 编解码挂起;优先保证 macOS 可用", "ported": [], "adapted": [], "deleted": [], "divergent": [], "needs_live": [], "decision": {"by": "user", "statement": "先不管这个问题了,先保证 macOS 能正常使用,Android 后期再想办法"}, "finding": {"symptom": "build-android.sh 默认 API 24 下 extension/src/net/text_codec.cpp 编译失败:use of undeclared identifier 'iconv_open' / 'iconv' / 'iconv_close'", "root_cause": "text_codec.cpp:10 用 __has_include(<iconv.h>) 探测。NDK 里该头在任何 API 等级都存在,但声明被 __BIONIC_AVAILABILITY_GUARD(28) 挡住,探测方式与 bionic 的可用性模型不匹配", "measured": "NDK 28.2.13676358 clang++ -fsyntax-only--target=aarch64-linux-android24 报 3 处 undeclaredandroid28 通过", "why_api_bump_is_wrong": "NDK iconv.h 注释写明 bionic 只支持 utf8/ascii/usascii/utf16be/utf16le/utf32be/utf32le/wchart,没有 GB2312/GBK。升到 28 后 iconv_open(\"GB2312\",\"UTF-8\") 运行期返回 -1encode_one()/decode_wire() 对所有输入都返回 false,连纯 ASCII 也被拒,比现有 #else 的 ASCII 回退分支(text_codec.cpp:104-127)更糟。附带成本:APK min_sdk 需 >=28config/android/pyconfig.h 是 API 24 下 configure 的(HAVE_LANGINFO_H 因 nl_langinfo 需 API 26 被关掉),要重跑 gen_pyconfig.sh android 并重验内建模块表", "future_fix": "1) 探测改为 #if __has_include(<iconv.h>) && !defined(__ANDROID__),不要写 __ANDROID_API__ >= 282) Android 自带 GBK<->Unicode 表(约 22k 码位)。40250 原客户端走 Windows MultiByteToWideChar(CP949/CP936),跨平台无对应物;macOS 能用只是系统 libiconv 恰好带 GBK"}, "docs": ["docs/PORT-PLAN.md 新增「已知问题:Android 的 GBK 编解码(挂起,2026-09-23)」小节"], "tests": "仅文档改动;诊断由 NDK clang++ -fsyntax-only 双 API 等级实测得出", "not_done": "text_codec.cpp 未改动;Android 扩展构建目前仍是红的"}
{"time": "2026-09-23T23:30:00Z", "event": "test_fix", "unit": "net 测试去 flakynet_classic_session_test + net_loopback_test", "ported": [], "adapted": [], "deleted": [], "divergent": [], "needs_live": [], "trigger": "macOS 全量 ctest 连续两次各挂 1 个 net 测试,且每次挂的不是同一个(第一次 net.loopback_flow,第二次 net.classic_session", "root_causes": ["accept_loopback(): poll() 的 `<= 0` 把 EINTR 当成对端从未连接;且单片 500ms 超时在有负载时不够。50 轮压测里 4 次失败全部源于此", "net_classic_session_test.cpp:570 的 pump 循环条件抄自上面的死端口用例,但此处 connect 会成功,stage 先经过 LoggingIn,循环提前一步退出", "同文件 5 处手写 pump 循环只给 10-20ms 等真实 loopback socket(非阻塞 connect 完成 / connect 被拒 / 对端 FIN", "net_loopback_test.cpp 10 处 run(..., N, []{return false;}) 是固定空转而非等待条件,等不够就断言失败"], "fixes": ["accept_loopback() 改为 EINTR 重试 + 5s 截止时间(listener 本就是非阻塞,超时后 accept 不会挂住)", "新增 pump_until(session, want, max_ms=2000),替换全部 5 处手写循环;等的是断言真正关心的那个状态", "替换 replacement PHASE_LOGIN 处的单次 pump(前一条 CHECK 已经断言过「不在同一帧」,这里只需要后续某次 pump 收到)", "net_loopback_test 的 10 处固定空转改为条件等待;服务端批量收包用该批最后一个包的标志作条件(TCP 有序,最后一个到了说明前面都到了),客户端事件用 decltype 声明的本地累加器在谓词里 drain"], "speedup": {"net_classic_session_test": "61.2s -> 3.5s:两处 this-*-host-must-not-resolve.invalid 换成带空标签的名字。本机解析器不按 RFC 6761 本地拒绝 .invalid,每次发往上游等满 30s 超时;空标签让 getaddrinfo 在语法层返回同一个 EAI_NONAME,实测 <10ms", "net_loopback_test": "固定空转改条件退出后单跑 0.94s", "ctest_total": "78.7s -> 8.7s"}, "tests": "net_classic_session_test 100/100(修复前 50 轮挂 4 轮);net_loopback_test 100/100(修复前单独跑第 7 轮即挂);全量 ctest 27/27port_map.py check 0 error;参考 EterPack.cpp 的 8 个 pack key 值逐个 git grep --cached -w,均未入库", "not_done": "只改测试,未改产品代码。这些都是测试自身的计时/错误处理缺陷,不是 mtnet 的行为问题"}
{"time": "2026-09-23T11:00:00Z", "event": "port_round", "unit": "PORT-PLAN 批次 2P step 4 (macOS): the 40250 script layer starts inside the Godot process", "ported": [], "adapted": ["UserInterface/UserInterface.cpp:RunMainScript -> extension/src/platform/ScriptLib/PythonBoot.cpp (Start/RunMainScript/RunLine/Stop; 40250 keeps CPythonLauncher on Main()'s stack, Godot owns this process so the launcher has to outlive the call that starts it)", "EterBase/Debug.cpp:LogBox, EterBase/Debug.cpp:LogBoxf -> extension/src/platform/EterBase/Debug.cpp (stderr plus LastLogBoxMessage(); ScriptLib Traceback() reports through LogBoxf after PyErr_Fetch has cleared the exception, so this is the only place a script traceback survives)"], "deleted": [], "divergent": ["RunMainScript RegisterCommandLine passes the command line through unchanged: the -cs/-ncs auto-login decoding needs EterBase/Utils.cpp SplitLine and EterLib/Util.cpp base64_decode, both declared but not ported (批次 2V1). Nothing in this host passes -cs today.", "RunMainScript drops '--pause-before-create-window' (system(\"pause\")) and the XTrap init check: no console and no XTrap on macOS/Android.", "Of the 37 module initializers RunMainScript calls, only initpack() is called — the other 36 are 2V slices."], "needs_live": [], "staging": {"native": "extension/src/platform/ScriptLib/PythonBoot.{h,cpp} (godot-free, placed under ScriptLib because that is the directory port_logic/port_platform drop when `mtpython` is absent; compiled with the UserInterface library's definitions so its `#ifdef _DISTRIBUTE` gives __DEBUG__ = 0). extension/src/platform/EterBase/LogBox.h. extension/src/python_host_node.{h,cpp} -> GDScript class Metin2PythonHost (start/is_running/run_main_script/run_line/stop; each returns \"\" or the reason, and a build without the interpreter answers with a distinguishable message instead of failing to register).", "gdscript": "project/python_host_check.gd in two halves — the interpreter + stdlib half needs no 40250 pack and runs on any platform, the system.py half needs the pack and is skipped without it. project/python_host_test.gd (--script) is the desktop entry; MT_TEST_MODE=python in project/client_main.gd is the on-device entry, since export templates do not accept --script.", "boot": "Metin2PythonHost.start(\"\") stages the stdlib through Metin2Python.stdlib_path(), which until now had no caller outside python_stdlib_test.gd."}, "status_note": "macOS only. Android/iOS in-process is untouched: the APK cross-build is blocked on the GBK iconv issue (see the PORT-PLAN known-issue section, deferred by the user 2026-09-23), and a device has no 40250 Client/pack, so on a phone only the interpreter + stdlib half of the check can run. prototype.RunApp() is 2V0: system.py still stops at its first import. The real boot path (AppFlow) does not start the interpreter.", "tests": ["godot --headless --path project --script python_host_test.gd -> PASS: interpreter starts in-process, is_running(), a second start() is a no-op, `import os, string, types, codecs`, u'\\xe4'.encode('cp1252'), sys.path non-empty, system.py stops at ImportError: No module named app (identical to ctest port.python_launcher), stop() tears it down", "MT_TEST_MODE=python godot --headless --path project -> same result through client_main.gd", "ctest --test-dir build -> 27/27 passed (14.51s)"], "not_done": "Android/iOS 进程内验证;正式启动路径起解释器(2V0);RunMainScript 的 -cs 命令行解码(2V1", "commit": "2P-step4-macos"}
{"time": "2026-09-23T11:40:00Z", "event": "port_round", "unit": "PORT-PLAN 批次 2V0-a: the EterPythonLib window-system headers enter the mirror", "ported": ["EterPythonLib/StdAfx.h", "EterPythonLib/PythonGraphic.h", "EterPythonLib/PythonWindow.h", "EterPythonLib/PythonWindowManager.h", "EterPythonLib/PythonSlotWindow.h", "EterPythonLib/PythonGridSlotWindow.h"], "adapted": [], "deleted": [], "divergent": [], "needs_live": [], "staging": {"scope": "headers only; no .cpp unit and therefore no port-map entry — declarations, nothing to call. The EterPythonLib .cpp units are 2V0-b.", "mechanical": "port_copy.py copy, 0 of 6 with manual edits: every one compiles as a mechanical CP949->UTF-8 / CRLF->LF / include-case copy.", "stdafx": "EterPythonLib/StdAfx.h had to come first: port_header_gate compiles each header behind its library's StdAfx.h, as 40250 does, and without it these five see neither <Python.h>, the STL, nor CSingleton.", "slice_list_correction": "audit/slices/2V0.json lists EterLib/Dimm.h as a missing header. It is not: 40250 vendors the MIDL output of the Windows SDK <dimm.h> as EterLib/Dimm.h, and extension/src/port/common/shim/win32/Dimm.h already shims it (a mirror copy shadows the shim for EterLib/IME.h's quoted include and breaks the build on 'rpc.h'). Copied, found, and removed again."}, "status_note": "2V0 entry state measured: of the slice's 212 headers 164 were already in the mirror, 48 were not (1 EterLib = the SDK shim above, 5 EterPythonLib = this round, 42 UserInterface = 2V0-d, all behind UserInterface/PythonApplication.h). Of the 10 units, 2 are ported (ScriptLib/PythonLauncher.cpp, UserInterface/PythonPackModule.cpp) and 8 are not.", "tests": ["cmake --build build --target port_header_gate -> the 6 new gate translation units compile (each header alone, twice, behind EterPythonLib/StdAfx.h)", "ctest --test-dir build -> 27/27 passed"], "not_done": "EterPythonLib/*.cpp2V0-b);wndMgr/grp/grpImage/grpText 模块(2V0-c);42 个 UserInterface 头 + PythonApplicationModule.cpp2V0-d", "commit": "2V0-a"}
+16
View File
@@ -385,6 +385,22 @@ extension/third_party/cpython-2.7.18/ # 静态库(2P 批次
`EterPythonLib/PythonWindow*.cpp``PythonWindowManager*.cpp``PythonGraphic*Module.cpp`
`PythonApplicationModule.cpp` 和所需 platform UI adapter;玩法模块先用桩。验收为 app 进程内运行 `system.py`,显示并可操作
Logo/Popup,输入、焦点、裁剪、文本和图片至少各有一个运行测试。
**入口状态(2026-09-23 实测)**`audit/slices/2V0.json` 的 212 个头里 164 个已在镜像中,缺 48 个;
10 个单元里 `ScriptLib/PythonLauncher.cpp``UserInterface/PythonPackModule.cpp` 已移植,缺 8 个。
(清单里的 `EterLib/Dimm.h` 是误报:那是 Windows SDK `<dimm.h>` 的 MIDL 产物,40250 把它放在 EterLib 下,
`port/common/shim/win32/Dimm.h` 已经顶掉了;镜像一份反而会遮住 shim,`IME.h` 就找不到 `rpc.h`。)
子步骤,按依赖从下往上:
| 子步骤 | 内容 | 状态 |
| --- | --- | --- |
| 2V0-a | `EterPythonLib/StdAfx.h` + 5 个窗口系统头进镜像,门禁绿 | 完成 |
| 2V0-b | `EterPythonLib/PythonWindow.cpp``PythonWindowManager.cpp`(窗口树逻辑) | 未做 |
| 2V0-c | `wndMgr` / `grp` / `grpImage` / `grpText` / `grpThing` 模块 | 未做 |
| 2V0-d | 42 个 `UserInterface` 头(都在 `PythonApplication.h` 后面)+ `PythonApplicationModule.cpp``app` | 未做 |
| 2V0-e | `dbg` 等桩模块补齐,`system.py` 跑到 `prototype.RunApp()` | 未做 |
| 2V0-f | Godot 渲染器上的 platform UI adapterLogo/Popup 可显示可操作 | 未做 |
2. **2V1 — 登录/选角**:加入 `PythonNetworkStream{,PhaseHandShake,PhaseLogin,PhaseSelect,PhaseLoading}.cpp`
`PythonNetworkStreamModule.cpp`、AccountConnector 及登录/选角脚本真实调用到的模块。验收为 FakeServer 离线完成 phase 顺序,
再以真服 smoke 证明登录、角色列表和选择进入 Loading;未验证的服务器分支记 `NEEDS_LIVE`
@@ -0,0 +1,68 @@
#pragma once
#include "../EterLib/GrpTextInstance.h"
#include "../EterLib/GrpMarkInstance.h"
#include "../EterLib/GrpImageInstance.h"
#include "../EterLib/GrpExpandedImageInstance.h"
#include "../EterGrnLib/ThingInstance.h"
class CPythonGraphic : public CScreen, public CSingleton<CPythonGraphic>
{
public:
CPythonGraphic();
virtual ~CPythonGraphic();
void Destroy();
void PushState();
void PopState();
LPDIRECT3D8 GetD3D();
float GetOrthoDepth();
void SetInterfaceRenderState();
void SetGameRenderState();
void SetCursorPosition(int x, int y);
void SetOmniLight();
void SetViewport(float fx, float fy, float fWidth, float fHeight);
void RestoreViewport();
long GenerateColor(float r, float g, float b, float a);
void RenderDownButton(float sx, float sy, float ex, float ey);
void RenderUpButton(float sx, float sy, float ex, float ey);
void RenderImage(CGraphicImageInstance* pImageInstance, float x, float y);
void RenderAlphaImage(CGraphicImageInstance* pImageInstance, float x, float y, float aLeft, float aRight);
void RenderCoolTimeBox(float fxCenter, float fyCenter, float fRadius, float fTime);
bool SaveJPEG(const char * pszFileName, LPBYTE pbyBuffer, UINT uWidth, UINT uHeight);
bool SaveScreenShot(const char *szFileName);
DWORD GetAvailableMemory();
void SetGamma(float fGammaFactor = 1.0f);
protected:
typedef struct SState
{
D3DXMATRIX matView;
D3DXMATRIX matProj;
} TState;
DWORD m_lightColor;
DWORD m_darkColor;
protected:
std::stack<TState> m_stateStack;
D3DXMATRIX m_SaveWorldMatrix;
CCullingManager m_CullingManager;
D3DVIEWPORT8 m_backupViewport;
float m_fOrthoDepth;
};
@@ -0,0 +1,40 @@
#pragma once
#include "PythonSlotWindow.h"
namespace UI
{
class CGridSlotWindow : public CSlotWindow
{
public:
static DWORD Type();
public:
CGridSlotWindow(PyObject * ppyObject);
virtual ~CGridSlotWindow();
void Destroy();
void ArrangeGridSlot(DWORD dwStartIndex, DWORD dwxCount, DWORD dwyCount, int ixSlotSize, int iySlotSize, int ixTemporarySize, int iyTemporarySize);
protected:
void __Initialize();
BOOL GetPickedSlotPointer(TSlot ** ppSlot);
BOOL GetPickedSlotList(int iWidth, int iHeight, std::list<TSlot*> * pSlotPointerList);
BOOL GetGridSlotPointer(int ix, int iy, TSlot ** ppSlot);
BOOL GetPickedGridSlotPosition(int ixLocal, int iyLocal, int * pix, int * piy);
BOOL CheckMoving(DWORD dwSlotNumber, DWORD dwItemIndex, const std::list<TSlot*> & c_rSlotList);
BOOL OnIsType(DWORD dwType);
void OnRefreshSlot();
void OnRenderPickingSlot();
protected:
DWORD m_dwxCount;
DWORD m_dwyCount;
std::vector<TSlot *> m_SlotVector;
};
};
@@ -0,0 +1,211 @@
#pragma once
#include "PythonWindow.h"
namespace UI
{
enum
{
ITEM_WIDTH = 32,
ITEM_HEIGHT = 32,
SLOT_NUMBER_NONE = 0xffffffff,
};
enum ESlotStyle
{
SLOT_STYLE_NONE,
SLOT_STYLE_PICK_UP,
SLOT_STYLE_SELECT,
};
enum ESlotState
{
SLOT_STATE_LOCK = (1 << 0),
SLOT_STATE_CANT_USE = (1 << 1),
SLOT_STATE_DISABLE = (1 << 2),
SLOT_STATE_ALWAYS_RENDER_COVER = (1 << 3), // 현재 Cover 버튼은 슬롯에 무언가 들어와 있을 때에만 렌더링 하는데, 이 flag가 있으면 빈 슬롯이어도 커버 렌더링
};
class CSlotWindow : public CWindow
{
public:
static DWORD Type();
public:
class CSlotButton;
class CCoverButton;
class CCoolTimeFinishEffect;
friend class CSlotButton;
friend class CCoverButton;
typedef struct SSlot
{
DWORD dwState;
DWORD dwSlotNumber;
DWORD dwCenterSlotNumber; // NOTE : 사이즈가 큰 아이템의 경우 아이템의 실제 위치 번호
DWORD dwItemIndex; // NOTE : 여기서 사용되는 Item이라는 단어는 좁은 개념의 것이 아닌,
BOOL isItem; // "슬롯의 내용물"이라는 포괄적인 개념어. 더 좋은 것이 있을까? - [levites]
// CoolTime
float fCoolTime;
float fStartCoolTime;
// Toggle
BOOL bActive;
int ixPosition;
int iyPosition;
int ixCellSize;
int iyCellSize;
BYTE byxPlacedItemSize;
BYTE byyPlacedItemSize;
CGraphicImageInstance * pInstance;
CNumberLine * pNumberLine;
bool bRenderBaseSlotImage;
CCoverButton * pCoverButton;
CSlotButton * pSlotButton;
CImageBox * pSignImage;
CAniImageBox * pFinishCoolTimeEffect;
} TSlot;
typedef std::list<TSlot> TSlotList;
typedef TSlotList::iterator TSlotListIterator;
public:
CSlotWindow(PyObject * ppyObject);
virtual ~CSlotWindow();
void Destroy();
// Manage Slot
void SetSlotType(DWORD dwType);
void SetSlotStyle(DWORD dwStyle);
void AppendSlot(DWORD dwIndex, int ixPosition, int iyPosition, int ixCellSize, int iyCellSize);
void SetCoverButton(DWORD dwIndex, const char * c_szUpImageName, const char * c_szOverImageName, const char * c_szDownImageName, const char * c_szDisableImageName, BOOL bLeftButtonEnable, BOOL bRightButtonEnable);
void SetSlotBaseImage(const char * c_szFileName, float fr, float fg, float fb, float fa);
void AppendSlotButton(const char * c_szUpImageName, const char * c_szOverImageName, const char * c_szDownImageName);
void AppendRequirementSignImage(const char * c_szImageName);
void EnableCoverButton(DWORD dwIndex);
void DisableCoverButton(DWORD dwIndex);
void SetAlwaysRenderCoverButton(DWORD dwIndex, bool bAlwaysRender = false);
void ShowSlotBaseImage(DWORD dwIndex);
void HideSlotBaseImage(DWORD dwIndex);
BOOL IsDisableCoverButton(DWORD dwIndex);
BOOL HasSlot(DWORD dwIndex);
void ClearAllSlot();
void ClearSlot(DWORD dwIndex);
void SetSlot(DWORD dwIndex, DWORD dwVirtualNumber, BYTE byWidth, BYTE byHeight, CGraphicImage * pImage, D3DXCOLOR& diffuseColor);
void SetSlotCount(DWORD dwIndex, DWORD dwCount);
void SetSlotCountNew(DWORD dwIndex, DWORD dwGrade, DWORD dwCount);
void SetSlotCoolTime(DWORD dwIndex, float fCoolTime, float fElapsedTime = 0.0f);
void ActivateSlot(DWORD dwIndex);
void DeactivateSlot(DWORD dwIndex);
void RefreshSlot();
DWORD GetSlotCount();
void LockSlot(DWORD dwIndex);
void UnlockSlot(DWORD dwIndex);
BOOL IsLockSlot(DWORD dwIndex);
void SetCantUseSlot(DWORD dwIndex);
void SetUseSlot(DWORD dwIndex);
BOOL IsCantUseSlot(DWORD dwIndex);
void EnableSlot(DWORD dwIndex);
void DisableSlot(DWORD dwIndex);
BOOL IsEnableSlot(DWORD dwIndex);
// Select
void ClearSelected();
void SelectSlot(DWORD dwSelectingIndex);
BOOL isSelectedSlot(DWORD dwIndex);
DWORD GetSelectedSlotCount();
DWORD GetSelectedSlotNumber(DWORD dwIndex);
// Slot Button
void ShowSlotButton(DWORD dwSlotNumber);
void HideAllSlotButton();
void OnPressedSlotButton(DWORD dwType, DWORD dwSlotNumber, BOOL isLeft = TRUE);
// Requirement Sign
void ShowRequirementSign(DWORD dwSlotNumber);
void HideRequirementSign(DWORD dwSlotNumber);
// ToolTip
BOOL OnOverInItem(DWORD dwSlotNumber);
void OnOverOutItem();
// For Usable Item
void SetUseMode(BOOL bFlag);
void SetUsableItem(BOOL bFlag);
// CallBack
void ReserveDestroyCoolTimeFinishEffect(DWORD dwSlotIndex);
protected:
void __Initialize();
void __CreateToggleSlotImage();
void __CreateSlotEnableEffect();
void __CreateFinishCoolTimeEffect(TSlot * pSlot);
void __CreateBaseImage(const char * c_szFileName, float fr, float fg, float fb, float fa);
void __DestroyToggleSlotImage();
void __DestroySlotEnableEffect();
void __DestroyFinishCoolTimeEffect(TSlot * pSlot);
void __DestroyBaseImage();
// Event
void OnUpdate();
void OnRender();
BOOL OnMouseLeftButtonDown();
BOOL OnMouseLeftButtonUp();
BOOL OnMouseRightButtonDown();
BOOL OnMouseLeftButtonDoubleClick();
void OnMouseOverOut();
void OnMouseOver();
void RenderSlotBaseImage();
void RenderLockedSlot();
virtual void OnRenderPickingSlot();
virtual void OnRenderSelectedSlot();
// Select
void OnSelectEmptySlot(int iSlotNumber);
void OnSelectItemSlot(int iSlotNumber);
void OnUnselectEmptySlot(int iSlotNumber);
void OnUnselectItemSlot(int iSlotNumber);
void OnUseSlot();
// Manage Slot
BOOL GetSlotPointer(DWORD dwIndex, TSlot ** ppSlot);
BOOL GetSelectedSlotPointer(TSlot ** ppSlot);
virtual BOOL GetPickedSlotPointer(TSlot ** ppSlot);
void ClearSlot(TSlot * pSlot);
virtual void OnRefreshSlot();
// ETC
BOOL OnIsType(DWORD dwType);
protected:
DWORD m_dwSlotType;
DWORD m_dwSlotStyle;
std::list<DWORD> m_dwSelectedSlotIndexList;
TSlotList m_SlotList;
DWORD m_dwToolTipSlotNumber;
BOOL m_isUseMode;
BOOL m_isUsableItem;
CGraphicImageInstance * m_pBaseImageInstance;
CImageBox * m_pToggleSlotImage;
CAniImageBox * m_pSlotActiveEffect;
std::deque<DWORD> m_ReserveDestroyEffectDeque;
};
};
@@ -0,0 +1,525 @@
#pragma once
#include "../EterBase/Utils.h"
namespace UI
{
class CWindow
{
public:
typedef std::list<CWindow *> TWindowContainer;
static DWORD Type();
BOOL IsType(DWORD dwType);
enum EHorizontalAlign
{
HORIZONTAL_ALIGN_LEFT = 0,
HORIZONTAL_ALIGN_CENTER = 1,
HORIZONTAL_ALIGN_RIGHT = 2,
};
enum EVerticalAlign
{
VERTICAL_ALIGN_TOP = 0,
VERTICAL_ALIGN_CENTER = 1,
VERTICAL_ALIGN_BOTTOM = 2,
};
enum EFlags
{
FLAG_MOVABLE = (1 << 0), // 움직일 수 있는 창
FLAG_LIMIT = (1 << 1), // 창이 화면을 벗어나지 않음
FLAG_SNAP = (1 << 2), // 스냅 될 수 있는 창
FLAG_DRAGABLE = (1 << 3),
FLAG_ATTACH = (1 << 4), // 완전히 부모에 붙어 있는 창 (For Drag / ex. ScriptWindow)
FLAG_RESTRICT_X = (1 << 5), // 좌우 이동 제한
FLAG_RESTRICT_Y = (1 << 6), // 상하 이동 제한
FLAG_NOT_CAPTURE = (1 << 7),
FLAG_FLOAT = (1 << 8), // 공중에 떠있어서 순서 재배치가 되는 창
FLAG_NOT_PICK = (1 << 9), // 마우스에 의해 Pick되지 않는 창
FLAG_IGNORE_SIZE = (1 << 10),
FLAG_RTL = (1 << 11), // Right-to-left
};
public:
CWindow(PyObject * ppyObject);
virtual ~CWindow();
void AddChild(CWindow * pWin);
void Clear();
void DestroyHandle();
void Update();
void Render();
void SetName(const char * c_szName);
const char * GetName() { return m_strName.c_str(); }
void SetSize(long width, long height);
long GetWidth() { return m_lWidth; }
long GetHeight() { return m_lHeight; }
void SetHorizontalAlign(DWORD dwAlign);
void SetVerticalAlign(DWORD dwAlign);
void SetPosition(long x, long y);
void GetPosition(long * plx, long * ply);
long GetPositionX( void ) const { return m_x; }
long GetPositionY( void ) const { return m_y; }
RECT & GetRect() { return m_rect; }
void GetLocalPosition(long & rlx, long & rly);
void GetMouseLocalPosition(long & rlx, long & rly);
long UpdateRect();
RECT & GetLimitBias() { return m_limitBiasRect; }
void SetLimitBias(long l, long r, long t, long b) { m_limitBiasRect.left = l, m_limitBiasRect.right = r, m_limitBiasRect.top = t, m_limitBiasRect.bottom = b; }
void Show();
void Hide();
bool IsShow() { return m_bShow; }
bool IsRendering();
bool HasParent() { return m_pParent ? true : false; }
bool HasChild() { return m_pChildList.empty() ? false : true; }
int GetChildCount() { return m_pChildList.size(); }
CWindow * GetRoot();
CWindow * GetParent();
bool IsChild(CWindow * pWin);
void DeleteChild(CWindow * pWin);
void SetTop(CWindow * pWin);
bool IsIn(long x, long y);
bool IsIn();
CWindow * PickWindow(long x, long y);
CWindow * PickTopWindow(long x, long y); // NOTE : Children으로 내려가지 않고 상위에서만
// 체크 하는 특화된 함수
void __RemoveReserveChildren();
void AddFlag(DWORD flag) { SET_BIT(m_dwFlag, flag); }
void RemoveFlag(DWORD flag) { REMOVE_BIT(m_dwFlag, flag); }
bool IsFlag(DWORD flag) { return (m_dwFlag & flag) ? true : false; }
/////////////////////////////////////
virtual void OnRender();
virtual void OnUpdate();
virtual void OnChangePosition(){}
virtual void OnSetFocus();
virtual void OnKillFocus();
virtual void OnMouseDrag(long lx, long ly);
virtual void OnMouseOverIn();
virtual void OnMouseOverOut();
virtual void OnMouseOver();
virtual void OnDrop();
virtual void OnTop();
virtual void OnIMEUpdate();
virtual void OnMoveWindow(long x, long y);
///////////////////////////////////////
BOOL RunIMETabEvent();
BOOL RunIMEReturnEvent();
BOOL RunIMEKeyDownEvent(int ikey);
CWindow * RunKeyDownEvent(int ikey);
BOOL RunKeyUpEvent(int ikey);
BOOL RunPressEscapeKeyEvent();
BOOL RunPressExitKeyEvent();
virtual BOOL OnIMETabEvent();
virtual BOOL OnIMEReturnEvent();
virtual BOOL OnIMEKeyDownEvent(int ikey);
virtual BOOL OnIMEChangeCodePage();
virtual BOOL OnIMEOpenCandidateListEvent();
virtual BOOL OnIMECloseCandidateListEvent();
virtual BOOL OnIMEOpenReadingWndEvent();
virtual BOOL OnIMECloseReadingWndEvent();
virtual BOOL OnMouseLeftButtonDown();
virtual BOOL OnMouseLeftButtonUp();
virtual BOOL OnMouseLeftButtonDoubleClick();
virtual BOOL OnMouseRightButtonDown();
virtual BOOL OnMouseRightButtonUp();
virtual BOOL OnMouseRightButtonDoubleClick();
virtual BOOL OnMouseMiddleButtonDown();
virtual BOOL OnMouseMiddleButtonUp();
virtual BOOL OnKeyDown(int ikey);
virtual BOOL OnKeyUp(int ikey);
virtual BOOL OnPressEscapeKey();
virtual BOOL OnPressExitKey();
///////////////////////////////////////
virtual void SetColor(DWORD dwColor){}
virtual BOOL OnIsType(DWORD dwType);
/////////////////////////////////////
virtual BOOL IsWindow() { return TRUE; }
/////////////////////////////////////
protected:
std::string m_strName;
EHorizontalAlign m_HorizontalAlign;
EVerticalAlign m_VerticalAlign;
long m_x, m_y; // X,Y 상대좌표
long m_lWidth, m_lHeight; // 크기
RECT m_rect; // Global 좌표
RECT m_limitBiasRect; // limit bias 값
bool m_bMovable;
bool m_bShow;
DWORD m_dwFlag;
PyObject * m_poHandler;
CWindow * m_pParent;
TWindowContainer m_pChildList;
BOOL m_isUpdatingChildren;
TWindowContainer m_pReserveChildList;
#ifdef _DEBUG
public:
DWORD DEBUG_dwCounter;
#endif
};
class CLayer : public CWindow
{
public:
CLayer(PyObject * ppyObject) : CWindow(ppyObject) {}
virtual ~CLayer() {}
BOOL IsWindow() { return FALSE; }
};
class CBox : public CWindow
{
public:
CBox(PyObject * ppyObject);
virtual ~CBox();
void SetColor(DWORD dwColor);
protected:
void OnRender();
protected:
DWORD m_dwColor;
};
class CBar : public CWindow
{
public:
CBar(PyObject * ppyObject);
virtual ~CBar();
void SetColor(DWORD dwColor);
protected:
void OnRender();
protected:
DWORD m_dwColor;
};
class CLine : public CWindow
{
public:
CLine(PyObject * ppyObject);
virtual ~CLine();
void SetColor(DWORD dwColor);
protected:
void OnRender();
protected:
DWORD m_dwColor;
};
class CBar3D : public CWindow
{
public:
static DWORD Type();
public:
CBar3D(PyObject * ppyObject);
virtual ~CBar3D();
void SetColor(DWORD dwLeft, DWORD dwRight, DWORD dwCenter);
protected:
void OnRender();
protected:
DWORD m_dwLeftColor;
DWORD m_dwRightColor;
DWORD m_dwCenterColor;
};
// Text
class CTextLine : public CWindow
{
public:
CTextLine(PyObject * ppyObject);
virtual ~CTextLine();
void SetMax(int iMax);
void SetHorizontalAlign(int iType);
void SetVerticalAlign(int iType);
void SetSecret(BOOL bFlag);
void SetOutline(BOOL bFlag);
void SetFeather(BOOL bFlag);
void SetMultiLine(BOOL bFlag);
void SetFontName(const char * c_szFontName);
void SetFontColor(DWORD dwColor);
void SetLimitWidth(float fWidth);
void ShowCursor();
void HideCursor();
int GetCursorPosition();
void SetText(const char * c_szText);
const char * GetText();
void GetTextSize(int* pnWidth, int* pnHeight);
protected:
void OnUpdate();
void OnRender();
void OnChangePosition();
virtual void OnSetText(const char * c_szText);
protected:
CGraphicTextInstance m_TextInstance;
};
class CNumberLine : public CWindow
{
public:
CNumberLine(PyObject * ppyObject);
CNumberLine(CWindow * pParent);
virtual ~CNumberLine();
void SetPath(const char * c_szPath);
void SetHorizontalAlign(int iType);
void SetNumber(const char * c_szNumber);
protected:
void ClearNumber();
void OnRender();
void OnChangePosition();
protected:
std::string m_strPath;
std::string m_strNumber;
std::vector<CGraphicImageInstance *> m_ImageInstanceVector;
int m_iHorizontalAlign;
DWORD m_dwWidthSummary;
};
// Image
class CImageBox : public CWindow
{
public:
CImageBox(PyObject * ppyObject);
virtual ~CImageBox();
BOOL LoadImage(const char * c_szFileName);
void SetDiffuseColor(float fr, float fg, float fb, float fa);
int GetWidth();
int GetHeight();
protected:
virtual void OnCreateInstance();
virtual void OnDestroyInstance();
virtual void OnUpdate();
virtual void OnRender();
void OnChangePosition();
protected:
CGraphicImageInstance * m_pImageInstance;
};
class CMarkBox : public CWindow
{
public:
CMarkBox(PyObject * ppyObject);
virtual ~CMarkBox();
void LoadImage(const char * c_szFilename);
void SetDiffuseColor(float fr, float fg, float fb, float fa);
void SetIndex(UINT uIndex);
void SetScale(FLOAT fScale);
protected:
virtual void OnCreateInstance();
virtual void OnDestroyInstance();
virtual void OnUpdate();
virtual void OnRender();
void OnChangePosition();
protected:
CGraphicMarkInstance * m_pMarkInstance;
};
class CExpandedImageBox : public CImageBox
{
public:
static DWORD Type();
public:
CExpandedImageBox(PyObject * ppyObject);
virtual ~CExpandedImageBox();
void SetScale(float fx, float fy);
void SetOrigin(float fx, float fy);
void SetRotation(float fRotation);
void SetRenderingRect(float fLeft, float fTop, float fRight, float fBottom);
void SetRenderingMode(int iMode);
protected:
void OnCreateInstance();
void OnDestroyInstance();
virtual void OnUpdate();
virtual void OnRender();
BOOL OnIsType(DWORD dwType);
};
class CAniImageBox : public CWindow
{
public:
static DWORD Type();
public:
CAniImageBox(PyObject * ppyObject);
virtual ~CAniImageBox();
void SetDelay(int iDelay);
void AppendImage(const char * c_szFileName);
void SetRenderingRect(float fLeft, float fTop, float fRight, float fBottom);
void SetRenderingMode(int iMode);
void ResetFrame();
protected:
void OnUpdate();
void OnRender();
void OnChangePosition();
virtual void OnEndFrame();
BOOL OnIsType(DWORD dwType);
protected:
BYTE m_bycurDelay;
BYTE m_byDelay;
BYTE m_bycurIndex;
std::vector<CGraphicExpandedImageInstance*> m_ImageVector;
};
// Button
class CButton : public CWindow
{
public:
CButton(PyObject * ppyObject);
virtual ~CButton();
BOOL SetUpVisual(const char * c_szFileName);
BOOL SetOverVisual(const char * c_szFileName);
BOOL SetDownVisual(const char * c_szFileName);
BOOL SetDisableVisual(const char * c_szFileName);
const char * GetUpVisualFileName();
const char * GetOverVisualFileName();
const char * GetDownVisualFileName();
void Flash();
void Enable();
void Disable();
void SetUp();
void Up();
void Over();
void Down();
BOOL IsDisable();
BOOL IsPressed();
protected:
void OnUpdate();
void OnRender();
void OnChangePosition();
BOOL OnMouseLeftButtonDown();
BOOL OnMouseLeftButtonDoubleClick();
BOOL OnMouseLeftButtonUp();
void OnMouseOverIn();
void OnMouseOverOut();
BOOL IsEnable();
void SetCurrentVisual(CGraphicImageInstance * pVisual);
protected:
BOOL m_bEnable;
BOOL m_isPressed;
BOOL m_isFlash;
CGraphicImageInstance * m_pcurVisual;
CGraphicImageInstance m_upVisual;
CGraphicImageInstance m_overVisual;
CGraphicImageInstance m_downVisual;
CGraphicImageInstance m_disableVisual;
};
class CRadioButton : public CButton
{
public:
CRadioButton(PyObject * ppyObject);
virtual ~CRadioButton();
protected:
BOOL OnMouseLeftButtonDown();
BOOL OnMouseLeftButtonUp();
void OnMouseOverIn();
void OnMouseOverOut();
};
class CToggleButton : public CButton
{
public:
CToggleButton(PyObject * ppyObject);
virtual ~CToggleButton();
protected:
BOOL OnMouseLeftButtonDown();
BOOL OnMouseLeftButtonUp();
void OnMouseOverIn();
void OnMouseOverOut();
};
class CDragButton : public CButton
{
public:
CDragButton(PyObject * ppyObject);
virtual ~CDragButton();
void SetRestrictMovementArea(int ix, int iy, int iwidth, int iheight);
protected:
void OnChangePosition();
void OnMouseOverIn();
void OnMouseOverOut();
protected:
RECT m_restrictArea;
};
};
extern BOOL g_bOutlineBoxEnable;
@@ -0,0 +1,187 @@
#pragma once
namespace UI
{
class CWindow;
class CWindowManager : public CSingleton<CWindowManager>
{
public:
typedef std::map<std::string, CWindow *> TLayerContainer;
typedef std::list<CWindow *> TWindowContainer;
typedef std::map<int, CWindow *> TKeyCaptureWindowMap;
public:
CWindowManager();
virtual ~CWindowManager();
void Destroy();
float GetAspect();
void SetScreenSize(long lWidth, long lHeight);
void SetResolution(int hres, int vres);
void GetResolution(long & rx, long & ry)
{
rx=m_iHres;
ry=m_iVres;
}
void SetMouseHandler(PyObject * poMouseHandler);
long GetScreenWidth() { return m_lWidth; }
long GetScreenHeight() { return m_lHeight; }
void GetMousePosition(long & rx, long & ry);
BOOL IsDragging();
CWindow * GetLockWindow() { return m_pLockWindow; }
CWindow * GetPointWindow() { return m_pPointWindow; }
bool IsFocus() { return (m_pActiveWindow || m_pLockWindow); }
bool IsFocusWindow(CWindow * pWindow) { return pWindow == m_pActiveWindow; }
void SetParent(CWindow * pWindow, CWindow * pParentWindow);
void SetPickAlways(CWindow * pWindow);
enum
{
WT_NORMAL,
WT_SLOT,
WT_GRIDSLOT,
WT_TEXTLINE,
WT_MARKBOX,
WT_IMAGEBOX,
WT_EXP_IMAGEBOX,
WT_ANI_IMAGEBOX,
WT_BUTTON,
WT_RATIOBUTTON,
WT_TOGGLEBUTTON,
WT_DRAGBUTTON,
WT_BOX,
WT_BAR,
WT_LINE,
WT_BAR3D,
WT_NUMLINE,
};
CWindow * RegisterWindow(PyObject * po, const char * c_szLayer);
CWindow * RegisterTypeWindow(PyObject * po, DWORD dwWndType, const char * c_szLayer);
CWindow * RegisterSlotWindow(PyObject * po, const char * c_szLayer);
CWindow * RegisterGridSlotWindow(PyObject * po, const char * c_szLayer);
CWindow * RegisterTextLine(PyObject * po, const char * c_szLayer);
CWindow * RegisterMarkBox(PyObject * po, const char * c_szLayer);
CWindow * RegisterImageBox(PyObject * po, const char * c_szLayer);
CWindow * RegisterExpandedImageBox(PyObject * po, const char * c_szLayer);
CWindow * RegisterAniImageBox(PyObject * po, const char * c_szLayer);
CWindow * RegisterButton(PyObject * po, const char * c_szLayer);
CWindow * RegisterRadioButton(PyObject * po, const char * c_szLayer);
CWindow * RegisterToggleButton(PyObject * po, const char * c_szLayer);
CWindow * RegisterDragButton(PyObject * po, const char * c_szLayer);
CWindow * RegisterBox(PyObject * po, const char * c_szLayer);
CWindow * RegisterBar(PyObject * po, const char * c_szLayer);
CWindow * RegisterLine(PyObject * po, const char * c_szLayer);
CWindow * RegisterBar3D(PyObject * po, const char * c_szLayer);
CWindow * RegisterNumberLine(PyObject * po, const char * c_szLayer);
void DestroyWindow(CWindow * pWin);
void NotifyDestroyWindow(CWindow * pWindow);
// Attaching Icon
BOOL IsAttaching();
DWORD GetAttachingType();
DWORD GetAttachingIndex();
DWORD GetAttachingSlotNumber();
void GetAttachingIconSize(BYTE * pbyWidth, BYTE * pbyHeight);
void AttachIcon(DWORD dwType, DWORD dwIndex, DWORD dwSlotNumber, BYTE byWidth, BYTE byHeight);
void DeattachIcon();
void SetAttachingFlag(BOOL bFlag);
// Attaching Icon
void OnceIgnoreMouseLeftButtonUpEvent();
void LockWindow(CWindow * pWin);
void UnlockWindow();
void ActivateWindow(CWindow * pWin);
void DeactivateWindow();
CWindow * GetActivateWindow();
void SetTop(CWindow * pWin);
void SetTopUIWindow();
void ResetCapture();
void Update();
void Render();
void RunMouseMove(long x, long y);
void RunMouseLeftButtonDown(long x, long y);
void RunMouseLeftButtonUp(long x, long y);
void RunMouseLeftButtonDoubleClick(long x, long y);
void RunMouseRightButtonDown(long x, long y);
void RunMouseRightButtonUp(long x, long y);
void RunMouseRightButtonDoubleClick(long x, long y);
void RunMouseMiddleButtonDown(long x, long y);
void RunMouseMiddleButtonUp(long x, long y);
void RunIMEUpdate();
void RunIMETabEvent();
void RunIMEReturnEvent();
void RunIMEKeyDown(int vkey);
void RunChangeCodePage();
void RunOpenCandidate();
void RunCloseCandidate();
void RunOpenReading();
void RunCloseReading();
void RunKeyDown(int vkey);
void RunKeyUp(int vkey);
void RunPressEscapeKey();
void RunPressExitKey();
private:
void SetMousePosition(long x, long y);
CWindow * __PickWindow(long x, long y);
CWindow * __NewWindow(PyObject * po, DWORD dwWndType);
void __ClearReserveDeleteWindowList();
private:
long m_lWidth;
long m_lHeight;
int m_iVres;
int m_iHres;
long m_lMouseX, m_lMouseY;
long m_lDragX, m_lDragY;
long m_lPickedX, m_lPickedY;
BOOL m_bOnceIgnoreMouseLeftButtonUpEventFlag;
int m_iIgnoreEndTime;
// Attaching Icon
PyObject * m_poMouseHandler;
BOOL m_bAttachingFlag;
DWORD m_dwAttachingType;
DWORD m_dwAttachingIndex;
DWORD m_dwAttachingSlotNumber;
BYTE m_byAttachingIconWidth;
BYTE m_byAttachingIconHeight;
// Attaching Icon
CWindow * m_pActiveWindow;
TWindowContainer m_ActiveWindowList;
CWindow * m_pLockWindow;
TWindowContainer m_LockWindowList;
CWindow * m_pPointWindow;
CWindow * m_pLeftCaptureWindow;
CWindow * m_pRightCaptureWindow;
CWindow * m_pMiddleCaptureWindow;
TKeyCaptureWindowMap m_KeyCaptureWindowMap;
TWindowContainer m_ReserveDeleteWindowList;
TWindowContainer m_PickAlwaysWindowList;
CWindow * m_pRootWindow;
TWindowContainer m_LayerWindowList;
TLayerContainer m_LayerWindowMap;
};
PyObject * BuildEmptyTuple();
};
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "../EterLib/StdAfx.h"
#include "../ScriptLib/StdAfx.h"
#include "PythonGraphic.h"
#include "PythonWindowManager.h"
void initgrp();
void initgrpImage();
void initgrpText();
void initgrpThing();
void initscriptWindow();
void initwndMgr();