Skip to content

Commit 8d225fe

Browse files
committed
Refactors icon extraction into a dedicated module
Moves icon extraction logic to a separate module to improve code organization and reusability. Simplifies the main tree implementation by removing redundant code and imports. Enhances maintainability by centralizing icon-related functionality.
1 parent f9af5b0 commit 8d225fe

2 files changed

Lines changed: 61 additions & 53 deletions

File tree

addon/globalPlugins/icon.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# objectViewer add-on for NVDA
2+
# This file is covered by the GNU General Public License.
3+
# See the file COPYING.txt for more details.
4+
# Copyright (C) 2024-2025 hwf1324 <1398969445@qq.com>
5+
6+
from ctypes import Structure, byref, c_char, c_int, sizeof, windll
7+
from ctypes.wintypes import DWORD, HANDLE, HICON
8+
9+
import wx
10+
11+
# Code modified from:
12+
# https://solutionfall.com/question/why-isnt-shgetfileinfow-modifying-the-reference-passed-into-the-function/
13+
SHGFI_ICON = 0x000000100
14+
SHGFI_SMALLICON = 0x000000001
15+
SHGFI_LARGEICON = 0x000000000
16+
SHGFI_ATTRIBUTES = 0x000000800
17+
SHIL_JUMBO = 0x0004
18+
19+
20+
class SHFILEINFO(Structure):
21+
_fields_ = [
22+
("hIcon", HANDLE),
23+
("iIcon", c_int),
24+
("dwAttributes", DWORD),
25+
("szDisplayName", c_char * 260),
26+
("szTypeName", c_char * 80),
27+
]
28+
29+
30+
def extractSmallHICON(pszPath: str) -> HICON:
31+
"""
32+
Extract the small icon from the specified `filename`, which might be
33+
either an executable or an `.ico` file.
34+
"""
35+
36+
psfi = SHFILEINFO()
37+
uFlags = SHGFI_ICON | SHGFI_SMALLICON
38+
windll.shell32.SHGetFileInfoW(pszPath, 0, byref(psfi), sizeof(psfi), uFlags)
39+
40+
return psfi.hIcon
41+
42+
43+
def cleanupHICON(hicon: HICON) -> None:
44+
windll.user32.DestroyIcon(hicon)
45+
46+
47+
# -----------------------------------------------------------------------------
48+
49+
50+
def createIconFromPath(path: str) -> wx.Icon | None:
51+
icon: wx.Icon = wx.Icon()
52+
hIcon: HICON = extractSmallHICON(path)
53+
if hIcon and icon.CreateFromHICON(hIcon):
54+
return icon
55+
56+
cleanupHICON(hIcon)
57+
return None

addon/globalPlugins/objectTree.py

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,71 +5,22 @@
55

66
import api
77
import config
8-
import shellapi
98
import wx
109
from NVDAObjects import NVDAObject
1110

11+
from .icon import createIconFromPath
1212
from .NVDAObjectIterator import ObjectIterator
1313

1414

15-
# Code modified from:
16-
# https://solutionfall.com/question/why-isnt-shgetfileinfow-modifying-the-reference-passed-into-the-function/
17-
SHGFI_ICON = 0x000000100
18-
SHGFI_SMALLICON = 0x000000001
19-
SHGFI_LARGEICON = 0x000000000
20-
SHGFI_ATTRIBUTES = 0x000000800
21-
SHIL_JUMBO = 0x0004
22-
23-
24-
class SHFILEINFO(shellapi.Structure):
25-
_fields_ = [
26-
("hIcon", shellapi.HANDLE),
27-
("iIcon", shellapi.c_int),
28-
("dwAttributes", shellapi.DWORD),
29-
("szDisplayName", shellapi.c_char * 260),
30-
("szTypeName", shellapi.c_char * 80),
31-
]
32-
33-
34-
def extractSmallHICON(pszPath: str) -> shellapi.HICON:
35-
"""
36-
Extract the small icon from the specified `filename`, which might be
37-
either an executable or an `.ico` file.
38-
"""
39-
40-
psfi = SHFILEINFO()
41-
uFlags = SHGFI_ICON | SHGFI_SMALLICON
42-
shellapi.shell32.SHGetFileInfoW(pszPath, 0, shellapi.byref(psfi), shellapi.sizeof(psfi), uFlags)
43-
44-
return psfi.hIcon
45-
46-
47-
def cleanupHICON(hicon: shellapi.HICON) -> None:
48-
shellapi.windll.user32.DestroyIcon(hicon)
49-
50-
51-
# -----------------------------------------------------------------------------
52-
53-
54-
def createIconFromPath(path: str) -> wx.Icon:
55-
icon: wx.Icon = wx.Icon()
56-
hIcon: shellapi.HICON = extractSmallHICON(path)
57-
if hIcon and icon.CreateFromHICON(hIcon):
58-
return icon
59-
60-
cleanupHICON(hIcon)
61-
return None
62-
63-
6415
class NVDAObjectTree(wx.TreeCtrl):
6516
def __init__(
6617
self,
67-
parent,
18+
parent: wx.Window,
6819
simpleReviewMode: bool = config.conf["reviewCursor"]["simpleReviewMode"],
6920
*args,
70-
**kw,
21+
**kwargs,
7122
):
72-
super().__init__(parent, *args, **kw)
23+
super().__init__(parent, *args, **kwargs)
7324
self.simpleReviewMode = simpleReviewMode
7425
rootNVDAObject: NVDAObject = api.getDesktopObject()
7526
imageDPISize: int = int(16 * self.GetDPIScaleFactor())

0 commit comments

Comments
 (0)