Skip to content

Commit

Permalink
Merge pull request #1 from Blue-Sky-Infomation-Factory/no-border-window
Browse files Browse the repository at this point in the history
Borderless window
  • Loading branch information
BSPR0002 authored Nov 17, 2024
2 parents 254e638 + 11c49a5 commit 8739c71
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 26 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: Python package

on:
release:
types: [published]
workflow_dispatch:

jobs:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name="bsif-webview2",
version="1.2.1",
version="1.3.0",
author="Blue Sky Infomation Factory",
description="Microsoft Webview2 for python.",
long_description=readme,
Expand Down
6 changes: 6 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from webview import WebViewApplication
from webview.webview import WebViewConfiguration

app = WebViewApplication(WebViewConfiguration(debug_enabled=True), "test")

app.start(borderless=True)
58 changes: 35 additions & 23 deletions webview/webview.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,36 @@
from os.path import dirname, join
from queue import Queue
from threading import current_thread, main_thread
from tkinter import Frame, Tk
from tkinter import Frame, Label, Tk
from tkinter.filedialog import askdirectory, askopenfilename, asksaveasfile
from traceback import print_exception
from typing import Any, Callable, Iterable, List, Optional, Tuple, TypedDict, Unpack
from win32gui import SetParent, MoveWindow
from win32gui import SetParent, MoveWindow, GetParent, SetWindowLong, GetWindowLong
from win32con import GWL_STYLE, WS_CAPTION, WS_THICKFRAME

from .bridge import Bridge, serialize_object
from .handlers import Handlers

clr.AddReference('System.Windows.Forms') # type: ignore
clr.AddReference('System.Threading') # type: ignore
clr.AddReference("System.Windows.Forms") # type: ignore
clr.AddReference("System.Threading") # type: ignore
self_path = dirname(__file__)
clr.AddReference(join(self_path, 'Microsoft.Web.WebView2.Core.dll')) # type: ignore
clr.AddReference(join(self_path, 'Microsoft.Web.WebView2.WinForms.dll')) # type: ignore
clr.AddReference(join(self_path, "Microsoft.Web.WebView2.Core.dll")) # type: ignore
clr.AddReference(join(self_path, "Microsoft.Web.WebView2.WinForms.dll")) # type: ignore
del self_path

from Microsoft.Web.WebView2.Core import CoreWebView2PermissionState, CoreWebView2HostResourceAccessKind # type: ignore
from Microsoft.Web.WebView2.WinForms import WebView2, CoreWebView2CreationProperties # type: ignore
from System import Uri # type: ignore
from System.Drawing import Color # type: ignore
from System.Threading import Thread, ApartmentState, ParameterizedThreadStart # type: ignore
from System.Windows.Forms import AnchorStyles, DockStyle # type: ignore

class WebViewStartParameters(TypedDict, total=False):
size: Optional[Tuple[int, int]]
position: Optional[Tuple[int, int]]
hide: bool
borderless: bool
background_transparent: bool # not implemented

class WebViewException(Exception):
def __init__(self, exception):
Expand All @@ -38,7 +42,7 @@ def __init__(self, exception):

class WebViewConfiguration:
def __init__(self,
data_folder: str = getenv('TEMP') + '/Microsoft WebView', # type: ignore
data_folder: str = getenv("TEMP") + "/Microsoft WebView", # type: ignore
private_mode = True,
debug_enabled = False,
user_agent:Optional[str] = None,
Expand Down Expand Up @@ -71,19 +75,19 @@ def __init__(self,

class WebViewApplication:

def __init__(self, configuration: WebViewConfiguration = WebViewConfiguration(), title = 'WebView Application'):
def __init__(self, configuration: WebViewConfiguration = WebViewConfiguration(), title = "WebView Application"):
self.__configuration = configuration
self.__thread: Optional[Thread] = None
self.__title = title
self.__root: Optional[Tk] = None
self.__frame: Optional[Frame] = None
self.__frame: Optional[Frame | Label] = None
self.__webview: Optional[WebView2] = None
self.__webview_hwnd: Optional[int] = None
self.__navigate_uri = "about:blank"
self.__message_handlers = Handlers()
self.__call_queue: Queue[Tuple[Callable, Tuple]] = Queue()

def __resize_webview(self, _):
def __resize_webview(self, *_):
assert self.__root and self.__frame and self.__webview_hwnd
frame = self.__frame
MoveWindow(self.__webview_hwnd, 0,0, frame.winfo_width(), frame.winfo_height(), False)
Expand All @@ -94,35 +98,43 @@ def __call_handler(self, _):
queue.task_done()
task[0](*task[1])

def __borderlessfy(self, *_):
root = self.__root
hwnd = GetParent(root.winfo_id()) # type: ignore
SetWindowLong(GetParent(root.winfo_id()), GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~(WS_CAPTION | WS_THICKFRAME)) # type: ignore
root.unbind("<Map>") # type: ignore

def __run(self, keywords: WebViewStartParameters):
configuration = self.__configuration
root = self.__root = Tk()
if keywords.get("borderless", False): root.bind("<Map>", self.__borderlessfy)
root.title(self.__title)
root.minsize(*configuration.min_size)
if configuration.max_size: root.maxsize(*configuration.max_size)
size=keywords.get("size")
position=keywords.get("position")
if size or position: root.geometry((f"{size[0]}x{size[1]}" if size else '') + (f"+{position[0]}+{position[1]}" if position else ""))
if size or position: root.geometry((f"{size[0]}x{size[1]}" if size else "") + (f"+{position[0]}+{position[1]}" if position else ""))
if keywords.get("hide"): root.withdraw()
frame = self.__frame = Frame(root)
frame.pack(fill="both",expand=True)
frame.configure(background="#FFF")
frame.pack(fill="both", expand=True)
frame_id = frame.winfo_id()
webview = self.__webview = WebView2()
webview_properties = CoreWebView2CreationProperties()
webview_properties.UserDataFolder = configuration.data_folder
webview_properties.set_IsInPrivateModeEnabled(configuration.private_mode)
webview_properties.AdditionalBrowserArguments = '--disable-features=ElasticOverscroll'
webview_properties.AdditionalBrowserArguments = "--disable-features=ElasticOverscroll"
webview.CreationProperties = webview_properties
webview.DefaultBackgroundColor = Color.White
webview.DefaultBackgroundColor = Color.Transparent
webview.CoreWebView2InitializationCompleted += self.__on_webview_ready
webview.NavigationStarting += self.__on_navigation_start
webview.NavigationCompleted += self.__on_navigation_completed
webview.WebMessageReceived += self.__on_javascript_message
webview.Source = Uri(self.__navigate_uri)
webview_handle = self.__webview_hwnd = webview.Handle.ToInt32()
SetParent(webview_handle, frame_id)
frame.bind('<Configure>', self.__resize_webview)
root.bind('<<AppCall>>', self.__call_handler)
frame.bind("<Configure>", self.__resize_webview)
root.bind("<<AppCall>>", self.__call_handler)
root.mainloop()
self.__root = self.__frame = self.__webview = self.__webview_hwnd = None

Expand Down Expand Up @@ -178,10 +190,10 @@ def __on_webview_ready(self, webview_instance, args):
if debug_enabled: core.OpenDevToolsWindow()

def __on_navigation_start(self, _, args):
print('Webview navigation started: ' + args.Uri)
print("Webview navigation started: " + args.Uri)

def __on_navigation_completed(self, _, args):
print('Webview navigation completed, status: ' + str(args.HttpStatusCode))
print("Webview navigation completed, status: " + str(args.HttpStatusCode))

def __on_permission_requested(self, _, args):
args.State = CoreWebView2PermissionState.Allow
Expand Down Expand Up @@ -249,24 +261,24 @@ def hide(self):
self.__root.withdraw()
def maximize(self):
assert self.__root, "WebView is not started."
self.__root.state('zoomed')
self.__root.state("zoomed")
def minimize(self):
assert self.__root, "WebView is not started."
self.__root.iconify()
def normalize(self):
assert self.__root, "WebView is not started."
self.__root.state('normal')
self.__root.state("normal")

@property
def is_fullscreen(self):
assert self.__root, "WebView is not started."
return bool(self.__root.attributes('-fullscreen'))
return bool(self.__root.attributes("-fullscreen"))
def fullscreen(self):
assert self.__root, "WebView is not started."
self.__root.attributes('-fullscreen', True)
self.__root.attributes("-fullscreen", True)
def exit_fullscreen(self):
assert self.__root, "WebView is not started."
self.__root.attributes('-fullscreen', False)
self.__root.attributes("-fullscreen", False)

def show_open_file_picker(
self,
Expand Down

0 comments on commit 8739c71

Please sign in to comment.