diff --git a/README.md b/README.md
index 619f68d..729a71a 100644
--- a/README.md
+++ b/README.md
@@ -287,6 +287,8 @@ run window, WindowEventsHandler(
)
```
+For a complete example with a custom title region, resize borders and a software-rendered close button, see [`examples/frameless_demo.nim`](examples/frameless_demo.nim).
+
all methods and events
see [siwin/platforms/any/window](https://github.com/levovix0/siwin/blob/master/src/siwin/platforms/any/window.nim)
diff --git a/examples/frameless_demo.nim b/examples/frameless_demo.nim
new file mode 100644
index 0000000..da19ff0
--- /dev/null
+++ b/examples/frameless_demo.nim
@@ -0,0 +1,305 @@
+import vmath
+import siwin
+import siwin/colorutils
+
+const
+ TitleBarHeight = 52'i32
+ CloseButtonSize = 40'i32
+ CloseButtonMargin = 12'i32
+ ResizeBorderWidth = 8'f32
+ ResizeCornerWidth = 18'f32
+
+ PopupSize = ivec2(520, 420)
+ ButtonSize = ivec2(220, 68)
+
+type
+ MainDemoState = object
+ rgbaBuffer: seq[Color32bit]
+ hoverButton: bool
+
+ PopupDemoState = object
+ rgbaBuffer: seq[Color32bit]
+ hoverClose: bool
+
+proc rgba(r, g, b: byte, a: byte = 255): Color32bit =
+ [r, g, b, a]
+
+proc insideRect(pos: Vec2, x, y, w, h: int32): bool =
+ pos.x >= x.float32 and pos.x < (x + w).float32 and pos.y >= y.float32 and
+ pos.y < (y + h).float32
+
+proc ensureBuffer[T](rgbaBuffer: var seq[T], size: IVec2) =
+ let pixelCount = max(1, (size.x * size.y).int)
+ if rgbaBuffer.len != pixelCount:
+ rgbaBuffer.setLen(pixelCount)
+
+proc fill(rgbaBuffer: var seq[Color32bit], size: IVec2, color: Color32bit) =
+ for i in 0 ..< size.x * size.y:
+ rgbaBuffer[i] = color
+
+proc fillRect(
+ rgbaBuffer: var seq[Color32bit], size: IVec2, x, y, w, h: int32, color: Color32bit
+) =
+ let
+ x0 = max(0, x)
+ y0 = max(0, y)
+ x1 = min(size.x, x + w)
+ y1 = min(size.y, y + h)
+
+ if x0 >= x1 or y0 >= y1:
+ return
+
+ for py in y0 ..< y1:
+ let row = py * size.x
+ for px in x0 ..< x1:
+ rgbaBuffer[row + px] = color
+
+proc popupButtonRect(size: IVec2): tuple[x, y, w, h: int32] =
+ (
+ x: max(28, (size.x - ButtonSize.x) div 2),
+ y: max(32, (size.y - ButtonSize.y) div 2),
+ w: ButtonSize.x,
+ h: ButtonSize.y,
+ )
+
+proc closeButtonRect(size: IVec2): tuple[x, y, w, h: int32] =
+ (
+ x: size.x - CloseButtonSize - CloseButtonMargin,
+ y: (TitleBarHeight - CloseButtonSize) div 2,
+ w: CloseButtonSize,
+ h: CloseButtonSize,
+ )
+
+proc popupPlacement(size: IVec2): PopupPlacement =
+ let buttonRect = popupButtonRect(size)
+ PopupPlacement(
+ anchorRectPos: ivec2(buttonRect.x, buttonRect.y),
+ anchorRectSize: ivec2(buttonRect.w, buttonRect.h),
+ size: PopupSize,
+ anchor: PopupAnchor.paBottomLeft,
+ gravity: PopupGravity.pgTopLeft,
+ offset: ivec2(0, 14),
+ constraintAdjustment: {
+ PopupConstraintAdjustment.pcaSlideX, PopupConstraintAdjustment.pcaFlipY,
+ PopupConstraintAdjustment.pcaResizeY,
+ },
+ reactive: true,
+ )
+
+proc applyPopupDragRegions(window: Window) =
+ let size = window.size
+ let titleWidth = max(1'i32, size.x - CloseButtonSize - CloseButtonMargin * 2)
+ window.setTitleRegion(vec2(0, 0), vec2(titleWidth.float32, TitleBarHeight.float32))
+ window.setBorderWidth(ResizeBorderWidth, 0, ResizeCornerWidth)
+
+proc drawMainWindow(state: var MainDemoState, size: IVec2, popupOpen: bool) =
+ state.rgbaBuffer.ensureBuffer(size)
+ state.rgbaBuffer.fill(size, rgba(245, 247, 250))
+
+ state.rgbaBuffer.fillRect(size, 48, 52, size.x - 96, 84, rgba(225, 231, 239))
+ state.rgbaBuffer.fillRect(size, 48, 164, size.x - 96, 132, rgba(255, 255, 255))
+ state.rgbaBuffer.fillRect(
+ size, 48, size.y - 132, size.x - 96, 72, rgba(225, 231, 239)
+ )
+
+ let buttonRect = popupButtonRect(size)
+ let buttonColor =
+ if popupOpen:
+ rgba(48, 103, 214)
+ elif state.hoverButton:
+ rgba(71, 125, 232)
+ else:
+ rgba(37, 88, 196)
+ state.rgbaBuffer.fillRect(
+ size, buttonRect.x, buttonRect.y, buttonRect.w, buttonRect.h, buttonColor
+ )
+ state.rgbaBuffer.fillRect(
+ size,
+ buttonRect.x + 6,
+ buttonRect.y + 6,
+ buttonRect.w - 12,
+ buttonRect.h - 12,
+ rgba(243, 247, 255),
+ )
+ state.rgbaBuffer.fillRect(
+ size,
+ buttonRect.x + 18,
+ buttonRect.y + 18,
+ buttonRect.w - 36,
+ buttonRect.h - 36,
+ buttonColor,
+ )
+
+proc drawCloseGlyph(
+ rgbaBuffer: var seq[Color32bit], size: IVec2, rect: tuple[x, y, w, h: int32]
+) =
+ let glyphInset = 12
+ for i in 0 ..< (rect.w - glyphInset * 2):
+ let x1 = rect.x + glyphInset + i
+ let y1 = rect.y + glyphInset + i
+ let x2 = rect.x + rect.w - glyphInset - 1 - i
+ let y2 = rect.y + glyphInset + i
+ if x1 >= 0 and x1 < size.x and y1 >= 0 and y1 < size.y:
+ rgbaBuffer[y1 * size.x + x1] = rgba(245, 247, 250)
+ if x2 >= 0 and x2 < size.x and y2 >= 0 and y2 < size.y:
+ rgbaBuffer[y2 * size.x + x2] = rgba(245, 247, 250)
+
+proc drawPopupWindow(state: var PopupDemoState, size: IVec2) =
+ state.rgbaBuffer.ensureBuffer(size)
+ state.rgbaBuffer.fill(size, rgba(238, 241, 245))
+
+ state.rgbaBuffer.fillRect(size, 0, 0, size.x, TitleBarHeight, rgba(23, 34, 46))
+ state.rgbaBuffer.fillRect(
+ size, 18, 82, size.x - 36, size.y - 100, rgba(255, 255, 255)
+ )
+ state.rgbaBuffer.fillRect(size, 42, 118, size.x - 84, 92, rgba(226, 232, 240))
+ state.rgbaBuffer.fillRect(size, 42, 232, size.x - 84, 92, rgba(212, 226, 255))
+ state.rgbaBuffer.fillRect(size, 42, 346, (size.x - 96) div 2, 42, rgba(255, 224, 201))
+ state.rgbaBuffer.fillRect(
+ size, size.x div 2 + 8, 346, (size.x - 96) div 2, 42, rgba(208, 244, 226)
+ )
+
+ let closeRect = closeButtonRect(size)
+ state.rgbaBuffer.fillRect(
+ size,
+ closeRect.x,
+ closeRect.y,
+ closeRect.w,
+ closeRect.h,
+ (if state.hoverClose: rgba(206, 62, 68) else: rgba(153, 33, 40)),
+ )
+ state.rgbaBuffer.drawCloseGlyph(size, closeRect)
+
+let globals = newSiwinGlobals()
+let window =
+ globals.newSoftwareRenderingWindow(size = ivec2(300, 200), title = "siwin popup demo")
+
+var
+ mainState: MainDemoState
+ popupState: PopupDemoState
+ popup: PopupWindow
+
+proc popupIsOpen(): bool =
+ popup != nil and popup.opened
+
+proc updatePopupPlacement() =
+ if popupIsOpen():
+ popup.reposition(window.size.popupPlacement())
+
+proc closePopup() =
+ if popupIsOpen():
+ popup.close()
+
+proc installPopupHandlers() =
+ popup.eventsHandler = WindowEventsHandler(
+ onResize: proc(e: ResizeEvent) =
+ e.window.applyPopupDragRegions()
+ redraw e.window
+ ,
+ onRender: proc(e: RenderEvent) =
+ let pixelBuffer = e.window.pixelBuffer
+ popupState.drawPopupWindow(pixelBuffer.size)
+ copyMem(
+ pixelBuffer.data,
+ popupState.rgbaBuffer[0].addr,
+ popupState.rgbaBuffer.len * sizeof(Color32bit),
+ )
+ convertPixelsInplace(
+ pixelBuffer.data, pixelBuffer.size, PixelBufferFormat.rgba_32bit,
+ pixelBuffer.format,
+ ),
+ onMouseMove: proc(e: MouseMoveEvent) =
+ let closeRect = closeButtonRect(e.window.size)
+ let newHover =
+ insideRect(e.pos, closeRect.x, closeRect.y, closeRect.w, closeRect.h)
+ if popupState.hoverClose != newHover:
+ popupState.hoverClose = newHover
+ redraw e.window
+ ,
+ onClick: proc(e: ClickEvent) =
+ let closeRect = closeButtonRect(e.window.size)
+ if e.button == MouseButton.left and
+ insideRect(e.pos, closeRect.x, closeRect.y, closeRect.w, closeRect.h):
+ e.window.close()
+ ,
+ onKey: proc(e: KeyEvent) =
+ if e.pressed and not e.generated and e.key == Key.escape:
+ e.window.close()
+ ,
+ onClose: proc(e: CloseEvent) =
+ popup = nil
+ popupState.hoverClose = false
+ redraw window
+ ,
+ )
+
+proc openPopup() =
+ if popupIsOpen():
+ return
+
+ popup = globals.newPopupWindow(window, window.size.popupPlacement(), grab = true)
+ popupState.hoverClose = false
+ popup.applyPopupDragRegions()
+ installPopupHandlers()
+ popup.firstStep(makeVisible = true)
+ redraw popup
+ redraw window
+
+window.eventsHandler = WindowEventsHandler(
+ onResize: proc(e: ResizeEvent) =
+ updatePopupPlacement()
+ redraw e.window
+ ,
+ onWindowMove: proc(e: WindowMoveEvent) =
+ updatePopupPlacement(),
+ onRender: proc(e: RenderEvent) =
+ let pixelBuffer = e.window.pixelBuffer
+ mainState.drawMainWindow(pixelBuffer.size, popupIsOpen())
+ copyMem(
+ pixelBuffer.data,
+ mainState.rgbaBuffer[0].addr,
+ mainState.rgbaBuffer.len * sizeof(Color32bit),
+ )
+ convertPixelsInplace(
+ pixelBuffer.data, pixelBuffer.size, PixelBufferFormat.rgba_32bit,
+ pixelBuffer.format,
+ ),
+ onMouseMove: proc(e: MouseMoveEvent) =
+ let buttonRect = popupButtonRect(e.window.size)
+ let newHover =
+ insideRect(e.pos, buttonRect.x, buttonRect.y, buttonRect.w, buttonRect.h)
+ if mainState.hoverButton != newHover:
+ mainState.hoverButton = newHover
+ redraw e.window
+ ,
+ onClick: proc(e: ClickEvent) =
+ let buttonRect = popupButtonRect(e.window.size)
+ if e.button == MouseButton.left and
+ insideRect(e.pos, buttonRect.x, buttonRect.y, buttonRect.w, buttonRect.h):
+ if popupIsOpen():
+ closePopup()
+ else:
+ openPopup()
+ ,
+ onClose: proc(e: CloseEvent) =
+ closePopup(),
+ onTick: proc(e: TickEvent) =
+ redraw e.window
+ if popupIsOpen():
+ redraw popup
+ ,
+ onKey: proc(e: KeyEvent) =
+ if e.pressed and not e.generated and e.key == Key.escape:
+ if popupIsOpen():
+ closePopup()
+ else:
+ close e.window
+ ,
+)
+
+window.firstStep(makeVisible = true)
+while window.opened or popupIsOpen():
+ if window.opened:
+ window.step()
+ if popupIsOpen():
+ popup.step()
diff --git a/src/siwin/platforms/any/window.nim b/src/siwin/platforms/any/window.nim
index f3717c3..ff01707 100644
--- a/src/siwin/platforms/any/window.nim
+++ b/src/siwin/platforms/any/window.nim
@@ -3,16 +3,18 @@ import pkg/[vmath]
import ../../[siwindefs, colorutils]
import ./[clipboards]
-
when siwin_use_pure_enums:
{.pragma: siwin_enum, pure.}
else:
{.pragma: siwin_enum.}
-
type
MouseButton* {.siwin_enum.} = enum
- left right middle forward backward
+ left
+ right
+ middle
+ forward
+ backward
ModifierKey* {.siwin_enum.} = enum
shift
@@ -24,37 +26,128 @@ type
Key* {.siwin_enum.} = enum
unknown = 0
-
- a b c d e f g h i j k l m n o p q r s t u v w x y z
- tilde n1 n2 n3 n4 n5 n6 n7 n8 n9 n0 minus equal
- f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14 f15
- lcontrol rcontrol lshift rshift lalt ralt lsystem rsystem lbracket rbracket
- space escape enter tab backspace menu
- slash dot comma semicolon quote backslash
-
- pageUp pageDown home End insert del
- left right up down
- npad0 npad1 npad2 npad3 npad4 npad5 npad6 npad7 npad8 npad9 npadDot
- add subtract multiply divide
- capsLock numLock scrollLock printScreen pause
-
- level3_shift level5_shift
-
+ a
+ b
+ c
+ d
+ e
+ f
+ g
+ h
+ i
+ j
+ k
+ l
+ m
+ n
+ o
+ p
+ q
+ r
+ s
+ t
+ u
+ v
+ w
+ x
+ y
+ z
+ tilde
+ n1
+ n2
+ n3
+ n4
+ n5
+ n6
+ n7
+ n8
+ n9
+ n0
+ minus
+ equal
+ f1
+ f2
+ f3
+ f4
+ f5
+ f6
+ f7
+ f8
+ f9
+ f10
+ f11
+ f12
+ f13
+ f14
+ f15
+ lcontrol
+ rcontrol
+ lshift
+ rshift
+ lalt
+ ralt
+ lsystem
+ rsystem
+ lbracket
+ rbracket
+ space
+ escape
+ enter
+ tab
+ backspace
+ menu
+ slash
+ dot
+ comma
+ semicolon
+ quote
+ backslash
+ pageUp
+ pageDown
+ home
+ End
+ insert
+ del
+ left
+ right
+ up
+ down
+ npad0
+ npad1
+ npad2
+ npad3
+ npad4
+ npad5
+ npad6
+ npad7
+ npad8
+ npad9
+ npadDot
+ add
+ subtract
+ multiply
+ divide
+ capsLock
+ numLock
+ scrollLock
+ printScreen
+ pause
+ level3_shift
+ level5_shift
TouchDeviceKind* {.siwin_enum.} = enum
touchScreen
touchPad
graphicsTablet
-
+
Touch* = ref object
- id*: int # begins at 1, increments for each new touch
+ id*: int # begins at 1, increments for each new touch
pos*: Vec2
pressed*: bool
- pressure*: float # 0..1
+ pressure*: float # 0..1
button*: Option[MouseButton]
device*: TouchDeviceKind
-
Mouse* = object
pos*: Vec2
pressed*: set[MouseButton]
@@ -62,10 +155,9 @@ type
Keyboard* = object
pressed*: set[Key]
modifiers*: set[ModifierKey]
-
+
TouchScreen* = object
- touches*: Table[int, Touch] # id -> touch
-
+ touches*: Table[int, Touch] # id -> touch
Edge* {.siwin_enum.} = enum
left
@@ -77,7 +169,6 @@ type
bottomLeft
bottomRight
-
CursorKind* {.siwin_enum.} = enum
builtin
image
@@ -88,70 +179,120 @@ type
of image: image*: ImageCursor
BuiltinCursor* {.siwin_enum.} = enum
- arrow arrowUp arrowRight
- wait arrowWait
- pointingHand grab
- text cross
- sizeAll sizeHorizontal sizeVertical
- sizeTopLeft sizeTopRight sizeBottomLeft sizeBottomRight
+ arrow
+ arrowUp
+ arrowRight
+ wait
+ arrowWait
+ pointingHand
+ grab
+ text
+ cross
+ sizeAll
+ sizeHorizontal
+ sizeVertical
+ sizeTopLeft
+ sizeTopRight
+ sizeBottomLeft
+ sizeBottomRight
hided
-
+
ImageCursor* = object
origin*: IVec2
pixels*: PixelBuffer
-
WindowTypeDefect* = object of Defect
## raised when trying to get pixel buffer from non-softwareRendering window
-
SiwinGlobals* = ref object of RootObj
-
Screen* = ref object of RootObj
-
MouseMoveKind* {.siwin_enum.} = enum
move
enter
leave
- moveWhileDragging ## (from this or other window)
+ moveWhileDragging ## (from this or other window)
-
DragStatus* {.siwin_enum.} = enum
rejected
accepted
+ PopupAnchor* {.siwin_enum.} = enum
+ paTopLeft
+ paTop
+ paTopRight
+ paLeft
+ paCenter
+ paRight
+ paBottomLeft
+ paBottom
+ paBottomRight
+
+ PopupGravity* {.siwin_enum.} = enum
+ pgTopLeft
+ pgTop
+ pgTopRight
+ pgLeft
+ pgCenter
+ pgRight
+ pgBottomLeft
+ pgBottom
+ pgBottomRight
+
+ PopupConstraintAdjustment* {.siwin_enum.} = enum
+ pcaSlideX
+ pcaSlideY
+ pcaFlipX
+ pcaFlipY
+ pcaResizeX
+ pcaResizeY
+
+ PopupDismissReason* {.siwin_enum.} = enum
+ pdrClientClosed
+ pdrCompositorDismissed
+ pdrParentClosed
+
+ PopupPlacement* = object
+ anchorRectPos*: IVec2
+ anchorRectSize*: IVec2
+ size*: IVec2
+ anchor*: PopupAnchor
+ gravity*: PopupGravity
+ offset*: IVec2
+ constraintAdjustment*: set[PopupConstraintAdjustment]
+ reactive*: bool
AnyWindowEvent* = object of RootObj
window*: Window
-
+
CloseEvent* = object of AnyWindowEvent
RenderEvent* = object of AnyWindowEvent
TickEvent* = object of AnyWindowEvent
deltaTime*: Duration
-
+
ResizeEvent* = object of AnyWindowEvent
size*: IVec2
initial*: bool
-
+
WindowMoveEvent* = object of AnyWindowEvent
pos*: IVec2
MouseMoveEvent* = object of AnyWindowEvent
pos*: Vec2
kind*: MouseMoveKind
-
+
MouseButtonEvent* = object of AnyWindowEvent
button*: MouseButton
pressed*: bool
- generated*: bool ## generated, for example, by releaseAllKeys when alt-tab. Means user don't actually do this action
-
+ generated*: bool
+ ## generated, for example, by releaseAllKeys when alt-tab. Means user don't actually do this action
+
ScrollEvent* = object of AnyWindowEvent
delta*: float
deltaX*: float
-
+
ClickEvent* = object of AnyWindowEvent
button*: MouseButton
pos*: Vec2
@@ -160,27 +301,29 @@ type
KeyEvent* = object of AnyWindowEvent
key*: Key
pressed*: bool
- repeated*: bool ## means user is holding this key and system is repeating keydown+keyup
- generated*: bool ## generated, for example, by releaseAllKeys when alt-tab. Means user don't actually do this action
+ repeated*: bool
+ ## means user is holding this key and system is repeating keydown+keyup
+ generated*: bool
+ ## generated, for example, by releaseAllKeys when alt-tab. Means user don't actually do this action
modifiers*: set[ModifierKey]
-
+
TextInputEvent* = object of AnyWindowEvent
text*: string
repeated*: bool
-
+
TouchEvent* = object of AnyWindowEvent
touch*: Touch
pressed*: bool
-
+
TouchMoveEvent* = object of AnyWindowEvent
touch*: Touch
kind*: MouseMoveKind
pos*: Vec2
-
+
TouchPressureChangedEvent* = object of AnyWindowEvent
touch*: Touch
- pressure*: float # 0..1
-
+ pressure*: float # 0..1
+
StateBoolChangedEventKind* {.siwin_enum.} = enum
focus
fullscreen
@@ -190,37 +333,49 @@ type
StateBoolChangedEvent* = object of AnyWindowEvent
value*: bool
kind*: StateBoolChangedEventKind
- isExternal*: bool ## changed by user via compositor (server-side change)
-
+ isExternal*: bool ## changed by user via compositor (server-side change)
- DropEvent* = object of AnyWindowEvent
+ PopupEvent* = object of AnyWindowEvent
+ reason*: PopupDismissReason
+ DropEvent* = object of AnyWindowEvent
WindowEventsHandler* = object
- onClose*: proc(e: CloseEvent) ## this window was closed (by pressing window close button, alt+f4, or by code)
- onRender*: proc(e: RenderEvent) ## this window is beeng redrawn, a full frame should be drawn on window until this callback finishes
- onTick*: proc(e: TickEvent) ## some time has passed and all pending events was handled
- onResize*: proc(e: ResizeEvent) ## this window changed it's width or height
- onWindowMove*: proc(e: WindowMoveEvent) ## this window changed it's position on screen
-
- onMouseMove*: proc(e: MouseMoveEvent) ## the mouse cursor changed it's position
- onMouseButton*: proc(e: MouseButtonEvent) ## a mouse button become pressed or released
- onScroll*: proc(e: ScrollEvent) ## a mouse wheel rotated (or scrolled by touchpad)
- onClick*: proc(e: ClickEvent) ## a mouse released a button without moving from position is was pressed this button
-
- onKey*: proc(e: KeyEvent) ## a key on a keyboard become pressed or released
- onTextInput*: proc(e: TextInputEvent) ## a (input method managed) unicode characters was inputed
-
- onTouch*: proc(e: TouchEvent) ## a touch either become pressed or released
- onTouchMove*: proc(e: TouchMoveEvent) ## a touch changed it's position (can be either pressed or released)
- onTouchPressureChanged*: proc(e: TouchPressureChangedEvent) ## a touch changed it's pressure (can be either pressed or released)
+ onClose*: proc(e: CloseEvent)
+ ## this window was closed (by pressing window close button, alt+f4, or by code)
+ onRender*: proc(e: RenderEvent)
+ ## this window is beeng redrawn, a full frame should be drawn on window until this callback finishes
+ onTick*: proc(e: TickEvent)
+ ## some time has passed and all pending events was handled
+ onResize*: proc(e: ResizeEvent) ## this window changed it's width or height
+ onWindowMove*: proc(e: WindowMoveEvent)
+ ## this window changed it's position on screen
+
+ onMouseMove*: proc(e: MouseMoveEvent) ## the mouse cursor changed it's position
+ onMouseButton*: proc(e: MouseButtonEvent)
+ ## a mouse button become pressed or released
+ onScroll*: proc(e: ScrollEvent) ## a mouse wheel rotated (or scrolled by touchpad)
+ onClick*: proc(e: ClickEvent)
+ ## a mouse released a button without moving from position is was pressed this button
+
+ onKey*: proc(e: KeyEvent) ## a key on a keyboard become pressed or released
+ onTextInput*: proc(e: TextInputEvent)
+ ## a (input method managed) unicode characters was inputed
+
+ onTouch*: proc(e: TouchEvent) ## a touch either become pressed or released
+ onTouchMove*: proc(e: TouchMoveEvent)
+ ## a touch changed it's position (can be either pressed or released)
+ onTouchPressureChanged*: proc(e: TouchPressureChangedEvent)
+ ## a touch changed it's pressure (can be either pressed or released)
onStateBoolChanged*: proc(e: StateBoolChangedEvent)
## binary state of focus/fullscreen/maximized/frameless changed
## fullscreen and maximized changes are sent before ResizeEvent
- onDrop*: proc(e: DropEvent) ## drag&drop clipboard content is beeng pasted to this window
+ onPopupDone*: proc(e: PopupEvent) ## popup was dismissed or explicitly closed
+ onDrop*: proc(e: DropEvent)
+ ## drag&drop clipboard content is beeng pasted to this window
Window* = ref object of RootObj
mouse*: Mouse
@@ -229,18 +384,23 @@ type
eventsHandler*: WindowEventsHandler
clicking: set[MouseButton]
-
+
redrawRequested: bool
lastTickTime: times.Time
m_closed: bool
-
+
m_transparent: bool
m_frameless: bool
m_cursor: Cursor
m_separateTouch: bool
-
+ m_isPopup: bool
+ m_popupGrab: bool
+ m_popupDismissed: bool
+ m_popupParent: Window
+ m_popupPlacement: PopupPlacement
+
m_size: IVec2
m_pos: IVec2
m_focused: bool
@@ -259,192 +419,342 @@ type
inputRegion, titleRegion: Option[tuple[pos, size: Vec2]]
borderWidth: Option[tuple[innerWidth, outerWidrth, diagonalSize: float32]]
+type PopupWindow* = Window
+
+func popupSize*(placement: PopupPlacement): IVec2 =
+ if placement.size.x > 0 and placement.size.y > 0:
+ placement.size
+ elif placement.anchorRectSize.x > 0 and placement.anchorRectSize.y > 0:
+ placement.anchorRectSize
+ else:
+ ivec2(1, 1)
+
+func popupAnchorOffset*(anchor: PopupAnchor, size: IVec2): IVec2 =
+ case anchor
+ of PopupAnchor.paTopLeft:
+ ivec2(0, 0)
+ of PopupAnchor.paTop:
+ ivec2(size.x div 2, 0)
+ of PopupAnchor.paTopRight:
+ ivec2(size.x, 0)
+ of PopupAnchor.paLeft:
+ ivec2(0, size.y div 2)
+ of PopupAnchor.paCenter:
+ ivec2(size.x div 2, size.y div 2)
+ of PopupAnchor.paRight:
+ ivec2(size.x, size.y div 2)
+ of PopupAnchor.paBottomLeft:
+ ivec2(0, size.y)
+ of PopupAnchor.paBottom:
+ ivec2(size.x div 2, size.y)
+ of PopupAnchor.paBottomRight:
+ ivec2(size.x, size.y)
+
+func popupAnchorOffset*(anchor: PopupGravity, size: IVec2): IVec2 =
+ case anchor
+ of PopupGravity.pgTopLeft:
+ ivec2(0, 0)
+ of PopupGravity.pgTop:
+ ivec2(size.x div 2, 0)
+ of PopupGravity.pgTopRight:
+ ivec2(size.x, 0)
+ of PopupGravity.pgLeft:
+ ivec2(0, size.y div 2)
+ of PopupGravity.pgCenter:
+ ivec2(size.x div 2, size.y div 2)
+ of PopupGravity.pgRight:
+ ivec2(size.x, size.y div 2)
+ of PopupGravity.pgBottomLeft:
+ ivec2(0, size.y)
+ of PopupGravity.pgBottom:
+ ivec2(size.x div 2, size.y)
+ of PopupGravity.pgBottomRight:
+ ivec2(size.x, size.y)
+
+func popupRelativePos*(placement: PopupPlacement): IVec2 =
+ let anchorPoint =
+ placement.anchorRectPos +
+ placement.anchor.popupAnchorOffset(placement.anchorRectSize)
+ anchorPoint - placement.gravity.popupAnchorOffset(placement.popupSize()) +
+ placement.offset
+
+method number*(screen: Screen): int32 {.base.} =
+ discard
+
+method width*(screen: Screen): int32 {.base.} =
+ discard
+
+method height*(screen: Screen): int32 {.base.} =
+ discard
+
+proc size*(screen: Screen): IVec2 =
+ ivec2(screen.width, screen.height)
+
+proc closed*(window: Window): bool =
+ window.m_closed
+
+proc opened*(window: Window): bool =
+ not window.closed
-method number*(screen: Screen): int32 {.base.} = discard
+method close*(window: Window) {.base.} =
+ ## request window close
+ window.m_closed = true
-method width*(screen: Screen): int32 {.base.} = discard
-method height*(screen: Screen): int32 {.base.} = discard
+proc transparent*(window: Window): bool =
+ window.m_transparent
-proc size*(screen: Screen): IVec2 = ivec2(screen.width, screen.height)
+proc frameless*(window: Window): bool =
+ window.m_frameless
+proc cursor*(window: Window): Cursor =
+ window.m_cursor
-proc closed*(window: Window): bool = window.m_closed
-proc opened*(window: Window): bool = not window.closed
+proc separateTouch*(window: Window): bool =
+ ## enable/disable handling touch events separately from mouse events
+ window.m_separateTouch
-method close*(window: Window) {.base.} =
- ## request window close
- window.m_closed = true
+proc isPopup*(window: Window): bool =
+ window.m_isPopup
-proc transparent*(window: Window): bool = window.m_transparent
-proc frameless*(window: Window): bool = window.m_frameless
-proc cursor*(window: Window): Cursor = window.m_cursor
-proc separateTouch*(window: Window): bool = window.m_separateTouch
- ## enable/disable handling touch events separately from mouse events
+proc popupGrab*(window: Window): bool =
+ window.m_popupGrab
-method reportedSize*(window: Window): IVec2 {.base.} = window.m_size
+method reportedSize*(window: Window): IVec2 {.base.} =
## Size reported to API users/events (backing pixels on HiDPI platforms).
+ window.m_size
-proc size*(window: Window): IVec2 = window.reportedSize()
-proc pos*(window: Window): IVec2 = window.m_pos
-proc fullscreen*(window: Window): bool = window.m_fullscreen
-proc maximized*(window: Window): bool = window.m_maximized
-proc minimized*(window: Window): bool = window.m_minimized
-proc visible*(window: Window): bool = window.m_visible
-proc resizable*(window: Window): bool = window.m_resizable
-proc minSize*(window: Window): IVec2 = window.m_minSize
-proc maxSize*(window: Window): IVec2 = window.m_maxSize
+proc size*(window: Window): IVec2 =
+ window.reportedSize()
-proc focused*(window: Window): bool = window.m_focused
+proc pos*(window: Window): IVec2 =
+ window.m_pos
-method uiScale*(window: Window): float32 {.base.} = 1'f32
- ## UI scale factor (device pixels per logical point).
+proc fullscreen*(window: Window): bool =
+ window.m_fullscreen
+proc maximized*(window: Window): bool =
+ window.m_maximized
-# note: locks: "unknown" usualy means that function can cause event outside of event loop
+proc minimized*(window: Window): bool =
+ window.m_minimized
+
+proc visible*(window: Window): bool =
+ window.m_visible
+
+proc resizable*(window: Window): bool =
+ window.m_resizable
+
+proc minSize*(window: Window): IVec2 =
+ window.m_minSize
+
+proc maxSize*(window: Window): IVec2 =
+ window.m_maxSize
+
+proc focused*(window: Window): bool =
+ window.m_focused
+
+method parentWindow*(window: Window): Window {.base.} =
+ window.m_popupParent
+
+method placement*(window: Window): PopupPlacement {.base.} =
+ window.m_popupPlacement
+
+proc popupOpen*(window: Window): bool =
+ window.opened and window.visible
+
+proc initPopupState*(window, parent: Window, placement: PopupPlacement, grab: bool) =
+ window.m_isPopup = true
+ window.m_popupGrab = grab
+ window.m_popupDismissed = false
+ window.m_popupParent = parent
+ window.m_popupPlacement = placement
+ window.m_size = placement.popupSize()
+
+proc notifyPopupDone*(window: Window, reason: PopupDismissReason) =
+ if not window.m_isPopup or window.m_popupDismissed:
+ return
+ window.m_popupDismissed = true
+ if window.eventsHandler.onPopupDone != nil:
+ window.eventsHandler.onPopupDone(PopupEvent(window: window, reason: reason))
+method uiScale*(window: Window): float32 {.base.} =
+ ## UI scale factor (device pixels per logical point).
+ 1'f32
+
+# note: locks: "unknown" usualy means that function can cause event outside of event loop
-method redraw*(window: Window) {.base.} = window.redrawRequested = true
- ## request render
+method redraw*(window: Window) {.base.} = ## request render
+ window.redrawRequested = true
-method `frameless=`*(window: Window, v: bool) {.base.} = discard
+method `frameless=`*(window: Window, v: bool) {.base.} =
+ discard
-method `cursor=`*(window: Window, v: Cursor) {.base.} = discard
+method `cursor=`*(window: Window, v: Cursor) {.base.} =
## set cursor
## used when mouse hover window
+ discard
-
-method `separateTouch=`*(window: Window, v: bool) {.base.} = discard
+method `separateTouch=`*(window: Window, v: bool) {.base.} =
## enable/disable handling touch events separately from mouse events
+ discard
-
-method `size=`*(window: Window, v: IVec2) {.base.} = discard
+method `size=`*(window: Window, v: IVec2) {.base.} =
## resize window
## exit fullscreen if window is fullscreen
+ discard
-method `pos=`*(window: Window, v: IVec2) {.base.} = discard
+method `pos=`*(window: Window, v: IVec2) {.base.} =
## move window
## do nothing if window is fullscreen
+ discard
-method `title=`*(window: Window, v: string) {.base.} = discard
- ## set window title
+method `title=`*(window: Window, v: string) {.base.} = ## set window title
+ discard
-method `fullscreen=`*(window: Window, v: bool) {.base.} = discard
+method `fullscreen=`*(window: Window, v: bool) {.base.} =
## fullscreen/unfullscreen window
+ discard
-method `maximized=`*(window: Window, v: bool) {.base.} = discard
+method `maximized=`*(window: Window, v: bool) {.base.} =
## maximize/unmaximize window
## exit fullscreen if window is fullscreen
+ discard
-method `minimized=`*(window: Window, v: bool) {.base.} = discard
- ## minimize/unminimize window
+method `minimized=`*(window: Window, v: bool) {.base.} = ## minimize/unminimize window
+ discard
-method `visible=`*(window: Window, v: bool) {.base.} = discard
- ## show/hide window
+method `visible=`*(window: Window, v: bool) {.base.} = ## show/hide window
+ discard
-method `resizable=`*(window: Window, v: bool) {.base.} = discard
- ## enable/disable resizing
+method `resizable=`*(window: Window, v: bool) {.base.} = ## enable/disable resizing
+ discard
-method `minSize=`*(window: Window, v: IVec2) {.base.} = discard
+method `minSize=`*(window: Window, v: IVec2) {.base.} =
## set minimum size
## `window.resizable=` will disable this
+ discard
-method `maxSize=`*(window: Window, v: IVec2) {.base.} = discard
+method `maxSize=`*(window: Window, v: IVec2) {.base.} =
## set maximum size
## `window.resizable=` will disable this
+ discard
-method canBecomeKeyWindow*(window: Window): bool {.base.} = true
+method `placement=`*(window: Window, v: PopupPlacement) {.base.} =
+ window.m_popupPlacement = v
+ let size = v.popupSize()
+ if window.m_size != size:
+ window.m_size = size
+
+method reposition*(window: Window, v: PopupPlacement) {.base.} =
+ window.placement = v
+
+method dismiss*(window: Window) {.base.} =
+ window.close()
+
+method canBecomeKeyWindow*(window: Window): bool {.base.} =
## whether this window is allowed to become key window.
## only macOS backend uses this property.
+ true
-method canBecomeMainWindow*(window: Window): bool {.base.} = true
+method canBecomeMainWindow*(window: Window): bool {.base.} =
## whether this window is allowed to become main window.
## only macOS backend uses this property.
+ true
-method `canBecomeKeyWindow=`*(window: Window, v: bool) {.base.} = discard
-method `canBecomeMainWindow=`*(window: Window, v: bool) {.base.} = discard
+method `canBecomeKeyWindow=`*(window: Window, v: bool) {.base.} =
+ discard
-method `icon=`*(window: Window, v: nil.typeof) {.base.} = discard
- ## clear window icon
+method `canBecomeMainWindow=`*(window: Window, v: bool) {.base.} =
+ discard
-method `icon=`*(window: Window, v: PixelBuffer) {.base.} = discard
- ## set window icon
+method `icon=`*(window: Window, v: nil.typeof) {.base.} = ## clear window icon
+ discard
+method `icon=`*(window: Window, v: PixelBuffer) {.base.} = ## set window icon
+ discard
-method startInteractiveMove*(window: Window, pos: Option[Vec2] = none Vec2) {.base.} = discard
+method startInteractiveMove*(window: Window, pos: Option[Vec2] = none Vec2) {.base.} =
## allow user to move window interactivly
## useful to create client-side decorated windows
## it's recomended to start interactive move after user grabbed window header and started to move mouse
+ discard
-
-method startInteractiveResize*(window: Window, edge: Edge, pos: Option[Vec2] = none Vec2) {.base.} = discard
+method startInteractiveResize*(
+ window: Window, edge: Edge, pos: Option[Vec2] = none Vec2
+) {.base.} =
## allow user to resize window interactivly
## useful to create client-side decorated windows
## it's recomended to start interactive resize after user grabbed window border and started to move mouse
+ discard
-
-method showWindowMenu*(window: Window, pos: Option[Vec2] = none Vec2) {.base.} = discard
+method showWindowMenu*(window: Window, pos: Option[Vec2] = none Vec2) {.base.} =
## show OS/platform/DE-specific window menu
## it's recomended to show menu after user right-clicked on window header
## for now works only on Linux(Wayland)
-
+ discard
method setInputRegion*(window: Window, pos, size: Vec2) {.base.} =
## set the rect (in window-local coordinates) where actual window is placed (inluding titlebar, if has one).
## this is used by Windows and Linux(Wayland) to correctly anchor the window and to correctly send mouse and touch events.
## it's recomended to set input region if you draw shadows for window.
## setInputRegion, if called once, must be called after each resize of the window
- assert size.x > 0 and size.y > 0, "there must be at least one pixel of the actual window"
+ assert size.x > 0 and size.y > 0,
+ "there must be at least one pixel of the actual window"
window.inputRegion = some (pos, size)
-
method setTitleRegion*(window: Window, pos, size: Vec2) {.base.} =
## set the rect (in window-local coordinates) where titlebar is placed.
## this is used by Windows to allow user to move window interactivly. siwin will replicate this behaviour on other platforms.
## it's recomended to set title region if you have custom titlebar.
window.titleRegion = some (pos, size)
-
-method setBorderWidth*(window: Window, innerWidth, outerWidth: float32, diagonalSize: float32) {.base.} =
+method setBorderWidth*(
+ window: Window, innerWidth, outerWidth: float32, diagonalSize: float32
+) {.base.} =
## set window border width. This will not change the look of window, it is for resizing window.
## this is used on Windows to allow user to resize window interactivly. siwin will replicate this behaviour on other platforms.
## it's recomended to set border width if you have custom titlebar.
window.borderWidth = some (innerWidth, outerWidth, diagonalSize)
-
method pixelBuffer*(window: Window): PixelBuffer {.base.} =
## returns pixel buffer attached to window
- raise WindowTypeDefect.newException("this Window has no pixel buffer. only SoftwareRendering windows have one")
-
+ raise WindowTypeDefect.newException(
+ "this Window has no pixel buffer. only SoftwareRendering windows have one"
+ )
-method makeCurrent*(window: Window) {.base.} = discard
+method makeCurrent*(window: Window) {.base.} =
## set window as current opengl rendering target
+ discard
-method `vsync=`*(window: Window, v: bool, silent = false) {.base.} = discard
+method `vsync=`*(window: Window, v: bool, silent = false) {.base.} =
## enable/disable vsync
+ discard
-
-method vulkanSurface*(window: Window): pointer {.base.} = discard
+method vulkanSurface*(window: Window): pointer {.base.} =
## get a VkSurfaceKHR attached to window
+ discard
+proc clipboard*(window: Window): Clipboard =
+ window.m_clipboard
-proc clipboard*(window: Window): Clipboard = window.m_clipboard
-
-proc selectionClipboard*(window: Window): Clipboard = window.m_selectionClipboard
-
-proc dragndropClipboard*(window: Window): Clipboard = window.m_dragndropClipboard
+proc selectionClipboard*(window: Window): Clipboard =
+ window.m_selectionClipboard
+proc dragndropClipboard*(window: Window): Clipboard =
+ window.m_dragndropClipboard
-method `dragStatus=`*(window: Window, v: DragStatus) {.base.} = discard
+method `dragStatus=`*(window: Window, v: DragStatus) {.base.} =
+ discard
-
-method firstStep*(window: Window, makeVisible = true) {.base.} = discard
+method firstStep*(window: Window, makeVisible = true) {.base.} =
## init window main loop
## don't call this proc if you will manage window events via run()
+ discard
-method step*(window: Window) {.base.} = discard
+method step*(window: Window) {.base.} =
## make window main loop step
## ! don't forget to call firstStep()
-
+ discard
proc run*(window: sink Window, makeVisible = true) =
## run whole window main loops
@@ -474,7 +784,11 @@ proc runMultiple*(windows: varargs[tuple[window: Window, makeVisible: bool]]) =
window.step()
inc i
-proc runMultiple*(windows: varargs[tuple[window: Window, eventsHandler: WindowEventsHandler, makeVisible: bool]]) =
+proc runMultiple*(
+ windows: varargs[
+ tuple[window: Window, eventsHandler: WindowEventsHandler, makeVisible: bool]
+ ]
+) =
## run for multiple windows
for (window, eventsHandler, makeVisible) in windows:
if eventsHandler != WindowEventsHandler():
diff --git a/src/siwin/platforms/cocoa/extras.nim b/src/siwin/platforms/cocoa/extras.nim
index 0f3f47d..a83dc1d 100644
--- a/src/siwin/platforms/cocoa/extras.nim
+++ b/src/siwin/platforms/cocoa/extras.nim
@@ -4,12 +4,20 @@ import pkg/darwin/objc/runtime
export app_kit, foundation, runtime
when not declared(activateIgnoringOtherApps):
- proc activateIgnoringOtherApps*(self: NSApplication, x: bool) {.objc: "activateIgnoringOtherApps:".}
+ proc activateIgnoringOtherApps*(
+ self: NSApplication, x: bool
+ ) {.objc: "activateIgnoringOtherApps:".}
when not compiles(screens(NSScreen)):
proc screens*(n: typedesc[NSScreen]): NSArray[NSScreen] {.objc: "screens".}
- proc registerForDraggedTypes*(self: NSView, types: NSArray[NSString]): NSArray[NSString] {.objc: "registerForDraggedTypes:".}
- proc draggingPasteboard*(self: NSDraggingInfo): NSPasteboard {.objc: "draggingPasteboard".}
+ proc registerForDraggedTypes*(
+ self: NSView, types: NSArray[NSString]
+ ): NSArray[NSString] {.objc: "registerForDraggedTypes:".}
+
+ proc draggingPasteboard*(
+ self: NSDraggingInfo
+ ): NSPasteboard {.objc: "draggingPasteboard".}
+
proc draggingLocation*(self: NSDraggingInfo): NSPoint {.objc: "draggingLocation".}
proc toggleFullScreen*(s: NSWindow, sender: ID) {.objc: "toggleFullScreen:".}
proc zoom*(s: NSWindow, sender: ID) {.objc: "zoom:".}
@@ -18,8 +26,20 @@ when not compiles(screens(NSScreen)):
proc deminiaturize*(s: NSWindow, sender: ID) {.objc: "deminiaturize:".}
proc setMinSize*(s: NSWindow, size: NSSize) {.objc: "setMinSize:".}
proc setMaxSize*(s: NSWindow, size: NSSize) {.objc: "setMaxSize:".}
+ proc addChildWindow*(
+ s: NSWindow, child: NSWindow, ordered: NSInteger
+ ) {.objc: "addChildWindow:ordered:".}
+
+ proc removeChildWindow*(s: NSWindow, child: NSWindow) {.objc: "removeChildWindow:".}
+ proc setExcludedFromWindowsMenu*(
+ s: NSWindow, excluded: BOOL
+ ) {.objc: "setExcludedFromWindowsMenu:".}
+
proc initWithSize*(self: NSImage, size: NSSize): NSImage {.objc: "initWithSize:".}
- proc addRepresentation*(self: NSImage, imageRep: NSImageRep) {.objc: "addRepresentation:".}
+ proc addRepresentation*(
+ self: NSImage, imageRep: NSImageRep
+ ) {.objc: "addRepresentation:".}
+
proc bitmapData*(self: NSBitmapImageRep): pointer {.objc.}
proc initWithBitmapDataPlanes*(
self: NSBitmapImageRep,
@@ -30,4 +50,7 @@ when not compiles(screens(NSScreen)):
colorSpaceName: NSString,
bitmapFormat: NSUInteger,
bytesPerRow, bitsPerPixel: NSInteger,
- ): NSBitmapImageRep {.objc: "initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:".}
+ ): NSBitmapImageRep {.
+ objc:
+ "initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:"
+ .}
diff --git a/src/siwin/platforms/cocoa/window.nim b/src/siwin/platforms/cocoa/window.nim
index 2629642..5b437b5 100644
--- a/src/siwin/platforms/cocoa/window.nim
+++ b/src/siwin/platforms/cocoa/window.nim
@@ -2,7 +2,8 @@ import std/[importutils, tables, times, os, unicode, uri, sequtils, strutils]
import pkg/[vmath]
from pkg/darwin/quartz_core/calayer import CALayer
from pkg/darwin/quartz_core/cametal_layer import CAMetalLayer
-from pkg/darwin/objc/runtime import ObjcClass, ID, SEL, alloc, new, addClass, selector, callSuper
+from pkg/darwin/objc/runtime import
+ ObjcClass, ID, SEL, alloc, new, addClass, selector, callSuper
import ../../[siwindefs]
import ../../[colorutils]
import ../any/[window {.all.}, clipboards]
@@ -33,7 +34,7 @@ type
lastDragStatus: DragStatus
m_canBecomeKeyWindow: bool
m_canBecomeMainWindow: bool
-
+
WindowCocoaSoftwareRendering* = ref object of WindowCocoa
softwareView: NSImageView
softwareImage: NSImage
@@ -50,13 +51,19 @@ type
ClipboardCocoaDnd* = ref object of Clipboard
activePasteboard: NSPasteboard
-
var
initialized: bool
- appDelegateClass, windowClass, softwareViewClass, openglViewClass, metalViewClass: ObjcClass
+ appDelegateClass, windowClass, softwareViewClass, openglViewClass, metalViewClass:
+ ObjcClass
windows: seq[WindowCocoa]
-proc init
+proc init()
+proc popupWindowPos(window: WindowCocoa, placement: PopupPlacement): IVec2 =
+ let parent = window.parentWindow()
+ if parent == nil:
+ placement.popupRelativePos()
+ else:
+ parent.pos + placement.popupRelativePos()
proc `=destroy`(window: WindowCocoaObj) {.siwin_destructor.} =
if window.addr[].m_closed:
@@ -78,11 +85,15 @@ proc `=destroy`(window: WindowCocoaObj) {.siwin_destructor.} =
if handle != nil:
try:
+ let parent = window.addr[].m_popupParent
+ if window.addr[].m_isPopup and parent != nil and parent of WindowCocoa:
+ let parentHandle = parent.WindowCocoa.handle
+ if parentHandle != nil:
+ parentHandle.removeChildWindow(handle)
close handle
except:
discard
-
proc findWindow(windows: seq[WindowCocoa], window: Id): WindowCocoa =
for w in windows:
if w == nil or w.handle == nil:
@@ -110,7 +121,6 @@ proc findWindow(windows: seq[WindowCocoa], window: Id): WindowCocoa =
if metalView != nil and cast[ID](metalView) == window:
return w
-
proc keycodeToKey(code: uint16): Key =
case code
of 0x1d: Key.n0
@@ -253,25 +263,26 @@ proc releaseAllInput(window: WindowCocoa) =
for key in window.keyboard.pressed:
window.keyboard.pressed.excl key
if window.eventsHandler.onKey != nil:
- window.eventsHandler.onKey(KeyEvent(
- window: window,
- key: key,
- pressed: false,
- repeated: false,
- generated: true,
- modifiers: window.keyboard.modifiers,
- ))
+ window.eventsHandler.onKey(
+ KeyEvent(
+ window: window,
+ key: key,
+ pressed: false,
+ repeated: false,
+ generated: true,
+ modifiers: window.keyboard.modifiers,
+ )
+ )
for button in window.mouse.pressed:
window.mouse.pressed.excl button
window.clicking.excl button
if window.eventsHandler.onMouseButton != nil:
- window.eventsHandler.onMouseButton(MouseButtonEvent(
- window: window,
- button: button,
- pressed: false,
- generated: true,
- ))
+ window.eventsHandler.onMouseButton(
+ MouseButtonEvent(
+ window: window, button: button, pressed: false, generated: true
+ )
+ )
const
CocoaMimeTextUriList = "text/uri-list"
@@ -301,19 +312,20 @@ proc stringToNSData(data: string): NSData =
copyMem(bytes[0].addr, data[0].unsafeAddr, data.len)
NSData.withBytes(bytes)
-proc emptyClipboardContent(kind: ClipboardContentKind, mimeType: string): ClipboardContent =
+proc emptyClipboardContent(
+ kind: ClipboardContentKind, mimeType: string
+): ClipboardContent =
if kind == ClipboardContentKind.other:
ClipboardContent(kind: ClipboardContentKind.other, mimeType: mimeType)
else:
ClipboardContent(kind: kind)
proc constructClipboardContent(
- data: sink string, kind: ClipboardContentKind, mimeType: string
+ data: sink string, kind: ClipboardContentKind, mimeType: string
): ClipboardContent =
case kind
of ClipboardContentKind.text:
ClipboardContent(kind: ClipboardContentKind.text, text: data)
-
of ClipboardContentKind.files:
let uris = data.splitLines
var files: seq[string]
@@ -329,7 +341,6 @@ proc constructClipboardContent(
files.add parsed.path
ClipboardContent(kind: ClipboardContentKind.files, files: files)
-
of ClipboardContentKind.other:
ClipboardContent(kind: ClipboardContentKind.other, mimeType: mimeType, data: data)
@@ -356,17 +367,13 @@ proc inferAvailableKinds(mimeTypes: openArray[string]): set[ClipboardContentKind
for mimeType in mimeTypes:
if mimeType in [
- "public.utf8-plain-text",
- "public.plain-text",
- "text/plain",
- "text/plain;charset=utf-8",
- "STRING",
- "TEXT",
- "NSStringPboardType",
+ "public.utf8-plain-text", "public.plain-text", "text/plain",
+ "text/plain;charset=utf-8", "STRING", "TEXT", "NSStringPboardType",
]:
result.incl ClipboardContentKind.text
- if mimeType in [CocoaMimeTextUriList, CocoaMimePublicFileUrl, CocoaMimeLegacyFileNames]:
+ if mimeType in
+ [CocoaMimeTextUriList, CocoaMimePublicFileUrl, CocoaMimeLegacyFileNames]:
result.incl ClipboardContentKind.files
if NSPasteboardTypeString != nil and $NSPasteboardTypeString in mimeTypes:
@@ -399,31 +406,27 @@ proc dataStringsForMimeType(pasteboard: NSPasteboard, mimeType: string): seq[str
result.add nsDataToString(data)
proc bestMimeType(
- availableMimeTypes: openArray[string], kind: ClipboardContentKind, mimeType: string
+ availableMimeTypes: openArray[string], kind: ClipboardContentKind, mimeType: string
): string =
case kind
of ClipboardContentKind.text:
let nativeType =
- if NSPasteboardTypeString != nil: $NSPasteboardTypeString
- else: "public.utf8-plain-text"
+ if NSPasteboardTypeString != nil:
+ $NSPasteboardTypeString
+ else:
+ "public.utf8-plain-text"
for candidate in [
- nativeType,
- "public.utf8-plain-text",
- "public.plain-text",
- "text/plain;charset=utf-8",
- "text/plain",
- "STRING",
- "TEXT",
- "NSStringPboardType",
+ nativeType, "public.utf8-plain-text", "public.plain-text",
+ "text/plain;charset=utf-8", "text/plain", "STRING", "TEXT", "NSStringPboardType",
]:
if candidate in availableMimeTypes:
return candidate
-
of ClipboardContentKind.files:
- for candidate in [CocoaMimeTextUriList, CocoaMimePublicFileUrl, CocoaMimeLegacyFileNames]:
+ for candidate in [
+ CocoaMimeTextUriList, CocoaMimePublicFileUrl, CocoaMimeLegacyFileNames
+ ]:
if candidate in availableMimeTypes:
return candidate
-
of ClipboardContentKind.other:
if mimeType in availableMimeTypes:
return mimeType
@@ -446,11 +449,13 @@ proc clearDragClipboard(clipboard: ClipboardCocoaDnd): bool =
proc notifyDragClipboardChanged(window: WindowCocoa) =
let clipboard = window.m_dragndropClipboard
if clipboard.onContentChanged != nil:
- clipboard.onContentChanged(ClipboardContentChangedEvent(
- clipboard: clipboard,
- availableKinds: clipboard.availableKinds,
- availableMimeTypes: clipboard.availableMimeTypes,
- ))
+ clipboard.onContentChanged(
+ ClipboardContentChangedEvent(
+ clipboard: clipboard,
+ availableKinds: clipboard.availableKinds,
+ availableMimeTypes: clipboard.availableMimeTypes,
+ )
+ )
proc updateDragClipboard(window: WindowCocoa, pasteboard: NSPasteboard) =
let clipboard = window.m_dragndropClipboard.ClipboardCocoaDnd
@@ -463,11 +468,7 @@ proc clearDragClipboard(window: WindowCocoa) =
window.notifyDragClipboardChanged()
proc dragOperation(status: DragStatus): NSDragOperation =
- if status == DragStatus.accepted:
- NSDragOperationCopy
- else:
- NSDragOperationNone
-
+ if status == DragStatus.accepted: NSDragOperationCopy else: NSDragOperationNone
proc screenCountCocoa*(): int32 =
let screens = NSScreen.screens()
@@ -483,7 +484,7 @@ proc defaultScreenCocoa*(): ScreenCocoa =
if screens != nil and screens.len > 0:
new result
if mainScreen != nil:
- for i in 0.. 0")
+ if window.isPopup:
+ var placement = window.placement
+ placement.size = v
+ window.placement = placement
+ return
+
if window.fullscreen:
window.fullscreen = false
@@ -788,15 +832,14 @@ method `size=`*(window: WindowCocoa, v: IVec2) =
let borderH = frame.size.height - contentRect.size.height
window.handle.setFrame(
NSMakeRect(
- frame.origin.x,
- frame.origin.y,
- v.x.float64 + borderW,
- v.y.float64 + borderH
+ frame.origin.x, frame.origin.y, v.x.float64 + borderW, v.y.float64 + borderH
),
- true
+ true,
)
method `pos=`*(window: WindowCocoa, v: IVec2) =
+ if window.isPopup:
+ return
if window.m_pos == v:
return
window.m_pos = v
@@ -811,10 +854,32 @@ method `pos=`*(window: WindowCocoa, v: IVec2) =
y = screenFrame.size.height - v.y.float64 - frame.size.height - 1
window.handle.setFrame(
- NSMakeRect(v.x.float64, y, frame.size.width, frame.size.height),
- true
+ NSMakeRect(v.x.float64, y, frame.size.width, frame.size.height), true
+ )
+
+method `placement=`*(window: WindowCocoa, v: PopupPlacement) =
+ window.m_popupPlacement = v
+ let size = v.popupSize()
+ window.m_size = size
+ let frame = window.handle.frame
+ let contentRect = window.handle.contentRectForFrameRect(frame)
+ let borderW = frame.size.width - contentRect.size.width
+ let borderH = frame.size.height - contentRect.size.height
+ let pos = window.popupWindowPos(v)
+ window.m_pos = pos
+ var y = frame.origin.y
+ let screen = window.handle.screen
+ if screen != nil:
+ let screenFrame = screen.frame
+ y = screenFrame.size.height - pos.y.float64 - (size.y.float64 + borderH) - 1
+ window.handle.setFrame(
+ NSMakeRect(pos.x.float64, y, size.x.float64 + borderW, size.y.float64 + borderH),
+ true,
)
+method reposition*(window: WindowCocoa, v: PopupPlacement) =
+ window.placement = v
+
method `fullscreen=`*(window: WindowCocoa, v: bool) =
if window.m_fullscreen == v:
return
@@ -822,14 +887,16 @@ method `fullscreen=`*(window: WindowCocoa, v: bool) =
if v and window.m_maximized:
window.m_maximized = false
- window.eventsHandler.pushEvent onStateBoolChanged, StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.maximized, value: false
- )
+ window.eventsHandler.pushEvent onStateBoolChanged,
+ StateBoolChangedEvent(
+ window: window, kind: StateBoolChangedEventKind.maximized, value: false
+ )
window.handle.toggleFullScreen(cast[ID](nil))
- window.eventsHandler.pushEvent onStateBoolChanged, StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.fullscreen, value: v
- )
+ window.eventsHandler.pushEvent onStateBoolChanged,
+ StateBoolChangedEvent(
+ window: window, kind: StateBoolChangedEventKind.fullscreen, value: v
+ )
method `maximized=`*(window: WindowCocoa, v: bool) =
if window.m_maximized == v:
@@ -841,9 +908,10 @@ method `maximized=`*(window: WindowCocoa, v: bool) =
if window.handle.isZoomed().bool != v:
window.handle.zoom(cast[ID](nil))
- window.eventsHandler.pushEvent onStateBoolChanged, StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.maximized, value: v
- )
+ window.eventsHandler.pushEvent onStateBoolChanged,
+ StateBoolChangedEvent(
+ window: window, kind: StateBoolChangedEventKind.maximized, value: v
+ )
method `minimized=`*(window: WindowCocoa, v: bool) =
if window.m_minimized == v:
@@ -861,11 +929,18 @@ method `resizable=`*(window: WindowCocoa, v: bool) =
window.m_resizable = v
window.handle.setStyleMask(
if window.m_frameless:
- if v: NSWindowStyleMaskMiniaturizable or NSWindowStyleMaskResizable or NSWindowStyleMaskBorderless
- else: NSWindowStyleMaskMiniaturizable or NSWindowStyleMaskBorderless
+ if v:
+ NSWindowStyleMaskMiniaturizable or NSWindowStyleMaskResizable or
+ NSWindowStyleMaskBorderless
+ else:
+ NSWindowStyleMaskMiniaturizable or NSWindowStyleMaskBorderless
else:
- if v: NSWindowStyleMaskMiniaturizable or NSWindowStyleMaskResizable or NSWindowStyleMaskTitled or NSWindowStyleMaskClosable
- else: NSWindowStyleMaskMiniaturizable or NSWindowStyleMaskTitled or NSWindowStyleMaskClosable
+ if v:
+ NSWindowStyleMaskMiniaturizable or NSWindowStyleMaskResizable or
+ NSWindowStyleMaskTitled or NSWindowStyleMaskClosable
+ else:
+ NSWindowStyleMaskMiniaturizable or NSWindowStyleMaskTitled or
+ NSWindowStyleMaskClosable
)
method `minSize=`*(window: WindowCocoa, v: IVec2) =
@@ -902,33 +977,39 @@ method `icon=`*(window: WindowCocoa, v: PixelBuffer) =
let sourceFormat = v.format
var buffer = v
- convertPixelsInplace(buffer.data, buffer.size, sourceFormat, PixelBufferFormat.rgba_32bit)
+ convertPixelsInplace(
+ buffer.data, buffer.size, sourceFormat, PixelBufferFormat.rgba_32bit
+ )
let rep = NSBitmapImageRep.alloc().initWithBitmapDataPlanes(
- nil,
- buffer.size.x.int,
- buffer.size.y.int,
- 8,
- 4,
- true,
- false,
- @"NSCalibratedRGBColorSpace",
- 0,
- (buffer.size.x * 4).int,
- 32
- )
+ nil,
+ buffer.size.x.int,
+ buffer.size.y.int,
+ 8,
+ 4,
+ true,
+ false,
+ @"NSCalibratedRGBColorSpace",
+ 0,
+ (buffer.size.x * 4).int,
+ 32,
+ )
if rep != nil:
copyMem(rep.bitmapData, buffer.data, buffer.size.x * buffer.size.y * 4)
- let img = NSImage.alloc().initWithSize(NSMakeSize(buffer.size.x.float64, buffer.size.y.float64))
+ let img = NSImage.alloc().initWithSize(
+ NSMakeSize(buffer.size.x.float64, buffer.size.y.float64)
+ )
img.addRepresentation(cast[NSImageRep](rep))
NSApplication.setApplicationIconImage(img)
rep.release()
img.release()
- convertPixelsInplace(buffer.data, buffer.size, PixelBufferFormat.rgba_32bit, sourceFormat)
+ convertPixelsInplace(
+ buffer.data, buffer.size, PixelBufferFormat.rgba_32bit, sourceFormat
+ )
method content*(
- clipboard: ClipboardCocoa, kind: ClipboardContentKind, mimeType: string
+ clipboard: ClipboardCocoa, kind: ClipboardContentKind, mimeType: string
): ClipboardContent =
autoreleasepool:
let pasteboard = NSPasteboard.withName(NSPasteboardNameGeneral)
@@ -945,9 +1026,13 @@ method content*(
of ClipboardContentKind.text:
return ClipboardContent(kind: ClipboardContentKind.text, text: payloads[0])
of ClipboardContentKind.files:
- return constructClipboardContent(payloads.join("\n"), ClipboardContentKind.files, targetType)
+ return constructClipboardContent(
+ payloads.join("\n"), ClipboardContentKind.files, targetType
+ )
of ClipboardContentKind.other:
- return ClipboardContent(kind: ClipboardContentKind.other, mimeType: targetType, data: payloads[0])
+ return ClipboardContent(
+ kind: ClipboardContentKind.other, mimeType: targetType, data: payloads[0]
+ )
method `content=`*(clipboard: ClipboardCocoa, content: ClipboardConvertableContent) =
autoreleasepool:
@@ -968,12 +1053,16 @@ method `content=`*(clipboard: ClipboardCocoa, content: ClipboardConvertableConte
case converted.kind
of ClipboardContentKind.text:
targetType =
- if NSPasteboardTypeString != nil: $NSPasteboardTypeString
- else: "public.utf8-plain-text"
+ if NSPasteboardTypeString != nil:
+ $NSPasteboardTypeString
+ else:
+ "public.utf8-plain-text"
serialized = converted.text
of ClipboardContentKind.files:
targetType = CocoaMimeTextUriList
- serialized = converted.files.mapIt($Uri(scheme: "file", path: it.encodeUrl(usePlus = false))).join("\n")
+ serialized = converted.files
+ .mapIt($Uri(scheme: "file", path: it.encodeUrl(usePlus = false)))
+ .join("\n")
of ClipboardContentKind.other:
targetType = converted.mimeType
serialized = converted.data
@@ -997,7 +1086,7 @@ method `content=`*(clipboard: ClipboardCocoa, content: ClipboardConvertableConte
clipboard.availableMimeTypes = availableMimeTypes
method content*(
- clipboard: ClipboardCocoaDnd, kind: ClipboardContentKind, mimeType: string
+ clipboard: ClipboardCocoaDnd, kind: ClipboardContentKind, mimeType: string
): ClipboardContent =
let pasteboard = clipboard.activePasteboard
if pasteboard == nil:
@@ -1016,9 +1105,13 @@ method content*(
of ClipboardContentKind.text:
result = ClipboardContent(kind: ClipboardContentKind.text, text: payloads[0])
of ClipboardContentKind.files:
- result = constructClipboardContent(payloads.join("\n"), ClipboardContentKind.files, targetType)
+ result = constructClipboardContent(
+ payloads.join("\n"), ClipboardContentKind.files, targetType
+ )
of ClipboardContentKind.other:
- result = ClipboardContent(kind: ClipboardContentKind.other, mimeType: targetType, data: payloads[0])
+ result = ClipboardContent(
+ kind: ClipboardContentKind.other, mimeType: targetType, data: payloads[0]
+ )
method `dragStatus=`*(window: WindowCocoa, v: DragStatus) =
window.lastDragStatus = v
@@ -1032,8 +1125,7 @@ proc swapBuffers*(window: WindowCocoaOpengl) =
method `vsync=`*(window: WindowCocoaOpengl, v: bool, silent = false) =
var swapInterval: int32 = if v: 1 else: 0
window.openglView.openGLContext.setValues(
- swapInterval.addr,
- NSOpenGLContextParameterSwapInterval
+ swapInterval.addr, NSOpenGLContextParameterSwapInterval
)
proc nativeWindowHandle*(window: WindowCocoa): pointer =
@@ -1047,15 +1139,17 @@ proc setContentViewLayer*(window: WindowCocoa, layerPtr: pointer) =
contentView.setWantsLayer(true)
contentView.setLayer(cast[CALayer](layerPtr))
-
-proc init =
- if initialized: return
- defer: initialized = true
+proc init() =
+ if initialized:
+ return
+ defer:
+ initialized = true
template getWindow(this: untyped) =
let window {.inject.} = windows.findWindow(this)
- if window == nil: return
-
+ if window == nil:
+ return
+
proc updateSize(window: WindowCocoa) =
let
contentView = window.handle.contentView
@@ -1066,9 +1160,10 @@ proc init =
window.m_size = size
if window of WindowCocoaSoftwareRendering:
window.WindowCocoaSoftwareRendering.resizeSoftwarePixelBuffer(size)
- window.eventsHandler.pushEvent onResize, ResizeEvent(window: window, size: window.m_size)
+ window.eventsHandler.pushEvent onResize,
+ ResizeEvent(window: window, size: window.m_size)
window.redrawRequested = true
-
+
proc scaledMousePos(window: WindowCocoa, location: NsPoint): Vec2 =
let contentView = window.handle.contentView
if contentView == nil:
@@ -1077,18 +1172,22 @@ proc init =
let
bounds = contentView.bounds
backingBounds = contentView.convertRectToBacking(bounds)
- backingPointRect = contentView.convertRectToBacking(NSMakeRect(location.x, location.y, 0, 0))
+ backingPointRect =
+ contentView.convertRectToBacking(NSMakeRect(location.x, location.y, 0, 0))
vec2(
backingPointRect.origin.x.float32,
- (backingBounds.size.height - backingPointRect.origin.y).float32
+ (backingBounds.size.height - backingPointRect.origin.y).float32,
)
proc updateMousePos(window: WindowCocoa, location: NsPoint, kind: MouseMoveKind) =
window.mouse.pos = scaledMousePos(window, location)
- window.eventsHandler.pushEvent onMouseMove, MouseMoveEvent(window: window, pos: window.mouse.pos, kind: kind)
+ window.eventsHandler.pushEvent onMouseMove,
+ MouseMoveEvent(window: window, pos: window.mouse.pos, kind: kind)
- proc handleMouseButton(window: WindowCocoa, button: MouseButton, pressed: bool, location: NsPoint) =
+ proc handleMouseButton(
+ window: WindowCocoa, button: MouseButton, pressed: bool, location: NsPoint
+ ) =
window.mouse.pos = scaledMousePos(window, location)
if pressed:
window.mouse.pressed.incl button
@@ -1098,420 +1197,502 @@ proc init =
window.mouse.pressed.excl button
if button in window.clicking:
- window.eventsHandler.pushEvent onClick, ClickEvent(
- window: window, button: button, pos: window.mouse.pos,
- double: (nows - window.lastClickTime[button]).inMilliseconds < 200
- )
+ window.eventsHandler.pushEvent onClick,
+ ClickEvent(
+ window: window,
+ button: button,
+ pos: window.mouse.pos,
+ double: (nows - window.lastClickTime[button]).inMilliseconds < 200,
+ )
window.clicking.excl button
-
+
window.lastClickTime[button] = nows
- window.eventsHandler.pushEvent onMouseButton, MouseButtonEvent(window: window, button: button, pressed: pressed)
+ window.eventsHandler.pushEvent onMouseButton,
+ MouseButtonEvent(window: window, button: button, pressed: pressed)
autoreleasepool:
discard NSApplication.sharedApplication()
addClass "SiwinAppDelegate", "NSObject", appDelegateClass:
- addMethod "applicationWillFinishLaunching:", proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
- let
- menuBar = NSMenu.alloc().init()
- appMenuItem = NSMenuItem.alloc().init()
- menuBar.addItem(appMenuItem)
- NSApp.setMainMenu(menuBar)
-
- let
- appMenu = NSMenu.alloc().init()
- processName = NSProcessInfo.processinfo.processName
- quitTitle = @("Quit " & $processName)
- quitMenuitem = NsMenuItem.alloc().initWithTitle(
- quitTitle,
- selector"terminate:",
- @"q"
- )
- appMenu.addItem(quitMenuItem)
- appMenuItem.setSubmenu(appMenu)
-
- addMethod "applicationDidFinishLaunching:", proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
- NSApp.setPresentationOptions(NSApplicationPresentationDefault)
- NSApp.setActivationPolicy(NSApplicationActivationPolicyRegular)
- NSApp.activateIgnoringOtherApps(true)
-
-
- addClass "SiwinWindow", "NSWindow", windowClass:
- addMethod "windowDidResize:", proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
- getWindow(self)
- updateSize window
-
- addMethod "windowDidMove:", proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
- getWindow(self)
- autoreleasepool:
+ addMethod "applicationWillFinishLaunching:",
+ proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
let
- windowFrame = window.handle.frame
- screenFrame = window.handle.screen.frame
- window.m_pos = vec2(
- windowFrame.origin.x,
- screenFrame.size.height - windowFrame.origin.y - windowFrame.size.height - 1
- ).ivec2
- window.eventsHandler.pushEvent onWindowMove, WindowMoveEvent(window: window, pos: window.m_pos)
-
- addMethod "canBecomeKeyWindow", proc(self: Id, cmd: Sel): bool {.cdecl.} =
- getWindow(self)
- window.canBecomeKeyWindow
-
- addMethod "canBecomeMainWindow", proc(self: Id, cmd: Sel): bool {.cdecl.} =
- getWindow(self)
- window.canBecomeMainWindow
-
- addMethod "windowDidBecomeKey:", proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
- getWindow(self)
- let contentView = window.handle.contentView
- if contentView != nil:
- discard window.handle.makeFirstResponder(contentView)
- window.m_focused = true
- window.refreshModifiers()
- window.eventsHandler.pushEvent onStateBoolChanged, StateBoolChangedEvent(
- window: window, value: true, kind: StateBoolChangedEventKind.focus
- )
- updateMousePos window, window.handle.mouseLocationOutsideOfEventStream, MouseMoveKind.enter
-
- addMethod "windowDidResignKey:", proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
- getWindow(self)
- updateMousePos window, window.handle.mouseLocationOutsideOfEventStream, MouseMoveKind.leave
- window.releaseAllInput()
- window.m_focused = false
- window.keyboard.modifiers = {}
- window.eventsHandler.pushEvent onStateBoolChanged, StateBoolChangedEvent(
- window: window, value: false, kind: StateBoolChangedEventKind.focus
- )
-
- addMethod "windowShouldClose:", proc(self: Id, cmd: Sel, notification: NsNotification): bool {.cdecl.} =
- getWindow(self)
- window.eventsHandler.pushEvent onClose, CloseEvent(window: window)
- destruct window
- true
+ menuBar = NSMenu.alloc().init()
+ appMenuItem = NSMenuItem.alloc().init()
+ menuBar.addItem(appMenuItem)
+ NSApp.setMainMenu(menuBar)
+ let
+ appMenu = NSMenu.alloc().init()
+ processName = NSProcessInfo.processinfo.processName
+ quitTitle = @("Quit " & $processName)
+ quitMenuitem =
+ NsMenuItem.alloc().initWithTitle(quitTitle, selector"terminate:", @"q")
+ appMenu.addItem(quitMenuItem)
+ appMenuItem.setSubmenu(appMenu)
+
+ addMethod "applicationDidFinishLaunching:",
+ proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
+ NSApp.setPresentationOptions(NSApplicationPresentationDefault)
+ NSApp.setActivationPolicy(NSApplicationActivationPolicyRegular)
+ NSApp.activateIgnoringOtherApps(true)
- template addSiwinViewClass(className, superName: string, viewClassRef: untyped) =
- addClass className, superName, viewClassRef:
- addProtocol "NSTextInputClient"
-
- addMethod "acceptsFirstResponder", proc(self: Id, cmd: Sel): bool {.cdecl.} =
- true
-
- addMethod "canBecomeKeyView", proc(self: Id, cmd: Sel): bool {.cdecl.} =
- true
-
- addMethod "acceptsFirstMouse:", proc(self: Id, cmd: Sel, event: NsEvent): bool {.cdecl.} =
- true
-
- addMethod "viewDidChangeBackingProperties", proc(self: Id, cmd: Sel): Id {.cdecl.} =
- discard callSuper(cast[NSObject](self), cmd)
+ addClass "SiwinWindow", "NSWindow", windowClass:
+ addMethod "windowDidResize:",
+ proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
getWindow(self)
updateSize window
-
- addMethod "updateTrackingAreas", proc(self: Id, cmd: Sel): Id {.cdecl.} =
- getWindow(self)
-
- if window.trackingArea != nil:
- cast[NSView](self).removeTrackingArea(window.trackingArea)
- window.trackingArea.release()
- window.trackingArea = nil
-
- window.trackingArea = NSTrackingArea.alloc().initWithRect(
- NSMakeRect(0, 0, 0, 0),
- NSTrackingMouseEnteredAndExited or NSTrackingMouseMoved or NSTrackingActiveInKeyWindow or
- NSTrackingCursorUpdate or NSTrackingInVisibleRect or NSTrackingAssumeInside,
- self, cast[ID](nil)
- )
-
- cast[NSView](self).addTrackingArea(window.trackingArea)
-
- callSuper(cast[NSObject](self), cmd)
-
- addMethod "draggingEntered:", proc(
- self: Id, cmd: Sel, sender: NSDraggingInfo
- ): NSDragOperation {.cdecl.} =
- getWindow(self)
- if sender == nil:
- return NSDragOperationNone
-
- window.lastDragStatus = DragStatus.rejected
- window.updateDragClipboard(sender.draggingPasteboard)
- updateMousePos(window, sender.draggingLocation, MouseMoveKind.moveWhileDragging)
- dragOperation(window.lastDragStatus)
-
- addMethod "draggingUpdated:", proc(
- self: Id, cmd: Sel, sender: NSDraggingInfo
- ): NSDragOperation {.cdecl.} =
- getWindow(self)
- if sender == nil:
- return NSDragOperationNone
-
- window.updateDragClipboard(sender.draggingPasteboard)
- updateMousePos(window, sender.draggingLocation, MouseMoveKind.moveWhileDragging)
- dragOperation(window.lastDragStatus)
-
- addMethod "draggingExited:", proc(
- self: Id, cmd: Sel, sender: NSDraggingInfo
- ): Id {.cdecl.} =
- getWindow(self)
- window.clearDragClipboard()
- window.lastDragStatus = DragStatus.rejected
-
- addMethod "performDragOperation:", proc(
- self: Id, cmd: Sel, sender: NSDraggingInfo
- ): bool {.cdecl.} =
- getWindow(self)
- if sender != nil:
- window.updateDragClipboard(sender.draggingPasteboard)
- if window.eventsHandler.onDrop != nil:
- window.eventsHandler.onDrop(DropEvent(window: window))
- let accepted = window.lastDragStatus == DragStatus.accepted
- window.clearDragClipboard()
- window.lastDragStatus = DragStatus.rejected
- accepted
-
- addMethod "mouseMoved:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
- getWindow(self)
- updateMousePos window, event.locationInWindow, MouseMoveKind.move
-
- addMethod "mouseDragged:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
- getWindow(self)
- updateMousePos window, event.locationInWindow, MouseMoveKind.move
-
- addMethod "rightMouseDragged:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
- getWindow(self)
- updateMousePos window, event.locationInWindow, MouseMoveKind.move
-
- addMethod "otherMouseDragged:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
- getWindow(self)
- updateMousePos window, event.locationInWindow, MouseMoveKind.move
-
- addMethod "scrollWheel:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
- getWindow(self)
- var
- deltaX = event.scrollingDeltaX
- deltaY = event.scrollingDeltaY
-
- if event.hasPreciseScrollingDeltas:
- deltaX *= 0.1
- deltaY *= 0.1
-
- if abs(deltaX) > 0 or abs(deltaY) > 0:
- window.eventsHandler.pushEvent onScroll, ScrollEvent(window: window, delta: deltaY, deltaX: deltaX)
-
- addMethod "mouseDown:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
- getWindow(self)
- window.handleMouseButton(MouseButton.left, true, event.locationInWindow)
-
- addMethod "mouseUp:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
- getWindow(self)
- window.handleMouseButton(MouseButton.left, false, event.locationInWindow)
-
- addMethod "rightMouseDown:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
- getWindow(self)
- window.handleMouseButton(MouseButton.right, true, event.locationInWindow)
-
- addMethod "rightMouseUp:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+
+ addMethod "windowDidMove:",
+ proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
getWindow(self)
- window.handleMouseButton(MouseButton.right, false, event.locationInWindow)
-
- addMethod "otherMouseDown:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ autoreleasepool:
+ let
+ windowFrame = window.handle.frame
+ screenFrame = window.handle.screen.frame
+ window.m_pos = vec2(
+ windowFrame.origin.x,
+ screenFrame.size.height - windowFrame.origin.y - windowFrame.size.height -
+ 1,
+ ).ivec2
+ window.eventsHandler.pushEvent onWindowMove,
+ WindowMoveEvent(window: window, pos: window.m_pos)
+
+ addMethod "canBecomeKeyWindow",
+ proc(self: Id, cmd: Sel): bool {.cdecl.} =
getWindow(self)
- case event.buttonNumber
- of 2: window.handleMouseButton(MouseButton.middle, true, event.locationInWindow)
- of 3: window.handleMouseButton(MouseButton.forward, true, event.locationInWindow)
- of 4: window.handleMouseButton(MouseButton.backward, true, event.locationInWindow)
- else: discard
-
- addMethod "otherMouseUp:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ window.canBecomeKeyWindow
+
+ addMethod "canBecomeMainWindow",
+ proc(self: Id, cmd: Sel): bool {.cdecl.} =
getWindow(self)
- case event.buttonNumber
- of 2: window.handleMouseButton(MouseButton.middle, false, event.locationInWindow)
- of 3: window.handleMouseButton(MouseButton.forward, false, event.locationInWindow)
- of 4: window.handleMouseButton(MouseButton.backward, false, event.locationInWindow)
- else: discard
-
- addMethod "keyDown:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ window.canBecomeMainWindow
+
+ addMethod "windowDidBecomeKey:",
+ proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
getWindow(self)
- let key = event.keyCode.keycodeToKey
- let modifiers = window.updateModifiers(event)
- if key != Key.unknown:
- window.keyboard.pressed.incl key
- window.eventsHandler.pushEvent onKey, KeyEvent(
- window: window,
- key: key,
- pressed: true,
- repeated: event.isARepeat.bool,
- modifiers: modifiers,
+ let contentView = window.handle.contentView
+ if contentView != nil:
+ discard window.handle.makeFirstResponder(contentView)
+ window.m_focused = true
+ window.refreshModifiers()
+ window.eventsHandler.pushEvent onStateBoolChanged,
+ StateBoolChangedEvent(
+ window: window, value: true, kind: StateBoolChangedEventKind.focus
)
+ updateMousePos window,
+ window.handle.mouseLocationOutsideOfEventStream, MouseMoveKind.enter
- # Let key events handle navigation/editing control keys directly;
- # routing them through NSTextInputClient can suppress following key presses.
- let shouldRouteToInputContext = key notin {
- Key.backspace,
- Key.del,
- Key.left,
- Key.right,
- Key.up,
- Key.down,
- Key.home,
- Key.End,
- Key.pageUp,
- Key.pageDown,
- Key.tab,
- Key.enter,
- Key.escape,
- }
- if window.eventsHandler.onTextInput != nil and shouldRouteToInputContext:
- var handledByInputContext = false
- let inputContext = cast[NSView](self).inputContext
- if inputContext != nil:
- handledByInputContext = inputContext.handleEvent(event).bool
-
- # Fallback for view classes where AppKit does not route insertText
- # through NSTextInputClient even though keyDown is delivered.
- if not handledByInputContext and (modifiers * {ModifierKey.control, ModifierKey.alt, ModifierKey.system}).len == 0:
- for r in ($event.characters).runes:
- if r.int >= 32 and r.int notin 0xf700..0xf7ff:
- window.eventsHandler.pushEvent onTextInput, TextInputEvent(
- window: window,
- text: $r,
- repeated: event.isARepeat.bool,
- )
-
- addMethod "keyUp:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ addMethod "windowDidResignKey:",
+ proc(self: Id, cmd: Sel, notification: NsNotification): Id {.cdecl.} =
getWindow(self)
- let key = event.keyCode.keycodeToKey
- let modifiers = window.updateModifiers(event)
- if key != Key.unknown:
- window.keyboard.pressed.excl key
- window.eventsHandler.pushEvent onKey, KeyEvent(window: window, key: key, pressed: false, repeated: false, modifiers: modifiers) # todo: handle repeated
-
- addMethod "flagsChanged:", proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
- #? wtf is this?!?
- getWindow(self)
- let key = event.keyCode.keycodeToKey
- let modifiers = window.updateModifiers(event)
- if key != Key.unknown:
- if key in window.keyboard.pressed:
- window.keyboard.pressed.excl key
- window.eventsHandler.pushEvent onKey, KeyEvent(window: window, key: key, pressed: false, repeated: false, modifiers: modifiers) # todo: handle repeated
- else:
- window.keyboard.pressed.incl key
- window.eventsHandler.pushEvent onKey, KeyEvent(window: window, key: key, pressed: true, repeated: false, modifiers: modifiers) # todo: handle repeated
-
- addMethod "hasMarkedText", proc(self: Id, cmd: Sel): bool {.cdecl.} =
- getWindow(self)
- window.markedText != nil
-
- addMethod "markedRange", proc(self: Id, cmd: Sel): NsRange {.cdecl.} =
- getWindow(self)
- if window.markedText != nil:
- NSMakeRange(0, window.markedText.length)
- else:
- NSRangeEmpty
-
- addMethod "selectedRange", proc(self: Id, cmd: Sel): NsRange {.cdecl.} =
- getWindow(self)
- NSMakeRange(0, 0)
-
- addMethod "setMarkedText:selectedRange:replacementRange:", proc(
- self: Id, cmd: Sel, obj: Id, selectedRange: NsRange, replacementRange: NsRange
- ): Id {.cdecl.} =
- getWindow(self)
- var characters: NSString
- if cast[NSObject](obj).isKindOfClass(NSAttributedString):
- characters = $(cast[NSAttributedString](obj).toNSString())
- else:
- characters = cast[NSString](obj)
-
- if window.markedText != nil:
- window.markedText.release()
-
- window.markedText = NSString.stringWithString(characters).retain()
-
- addMethod "unmarkText", proc(self: Id, cmd: Sel): Id {.cdecl.} =
- discard
-
- addMethod "validAttributesForMarkedText", proc(self: Id, cmd: Sel): NSArrayAbstract {.cdecl.} =
- cast[NSArrayAbstract](nil)
-
- addMethod "attributedSubstringForProposedRange:actualRange:", proc(
- self: Id, cmd: Sel, range: NsRange, actualRange: NsRangePointer
- ): ID =
- discard
-
- addMethod "insertText:replacementRange:", proc(
- self: Id, cmd: Sel, obj: Id, replacementRange: NsRange
- ): Id {.cdecl.} =
- getWindow(self)
- var characters: NSString
- if cast[NSObject](obj).isKindOfClass(NSAttributedString):
- characters = $cast[NSAttributedString](obj).toNSString()
- else:
- characters = cast[NSString](obj)
-
- var range = NSMakeRange(0, characters.length.uint)
- while range.length > 0:
- var
- codepoint: uint32
- usedLength: uint
- discard characters.getBytes(
- codepoint.addr,
- sizeof(codepoint).uint,
- usedLength.addr,
- NSUTF32StringEncoding,
- 0.NSStringEncodingConversionOptions,
- range,
- range.addr
+ updateMousePos window,
+ window.handle.mouseLocationOutsideOfEventStream, MouseMoveKind.leave
+ window.releaseAllInput()
+ window.m_focused = false
+ window.keyboard.modifiers = {}
+ window.eventsHandler.pushEvent onStateBoolChanged,
+ StateBoolChangedEvent(
+ window: window, value: false, kind: StateBoolChangedEventKind.focus
)
- if codepoint >= 0xf700 and codepoint <= 0xf7ff:
- continue
- window.eventsHandler.pushEvent onTextInput, TextInputEvent(window: window, text: $Rune(codepoint))
-
- if window.markedText != nil:
- window.markedText.release()
- window.markedText = nil
-
- addMethod "characterIndexForPoint:", proc(self: Id, cmd: Sel, point: NsPoint): uint {.cdecl.} =
- NSNotFound.uint
-
- addMethod "firstRectForCharacterRange:actualRange:", proc(
- self: Id, cmd: Sel, range: NsRange, actualRange: NsRangePointer
- ): NsRect {.cdecl.} =
- getWindow(self)
- let contentRect = window.handle.contentRectForFrameRect(window.handle.frame)
- NSMakeRect(
- contentRect.origin.x + 0,
- contentRect.origin.y + contentRect.size.height - 1 - 0,
- 0,
- 0
- )
-
- addMethod "doCommandBySelector:", proc(self: Id, cmd: Sel, selector: Sel): Id {.cdecl.} =
- discard
-
- addMethod "resetCursorRects", proc(self: Id, cmd: Sel): Id {.cdecl.} =
+
+ addMethod "windowShouldClose:",
+ proc(self: Id, cmd: Sel, notification: NsNotification): bool {.cdecl.} =
getWindow(self)
- autoreleasepool:
- case window.m_cursor.kind:
- of builtin:
+ window.eventsHandler.pushEvent onClose, CloseEvent(window: window)
+ destruct window
+ true
+
+ template addSiwinViewClass(className, superName: string, viewClassRef: untyped) =
+ addClass className, superName, viewClassRef:
+ addProtocol "NSTextInputClient"
+
+ addMethod "acceptsFirstResponder",
+ proc(self: Id, cmd: Sel): bool {.cdecl.} =
+ true
+
+ addMethod "canBecomeKeyView",
+ proc(self: Id, cmd: Sel): bool {.cdecl.} =
+ true
+
+ addMethod "acceptsFirstMouse:",
+ proc(self: Id, cmd: Sel, event: NsEvent): bool {.cdecl.} =
+ true
+
+ addMethod "viewDidChangeBackingProperties",
+ proc(self: Id, cmd: Sel): Id {.cdecl.} =
+ discard callSuper(cast[NSObject](self), cmd)
+ getWindow(self)
+ updateSize window
+
+ addMethod "updateTrackingAreas",
+ proc(self: Id, cmd: Sel): Id {.cdecl.} =
+ getWindow(self)
+
+ if window.trackingArea != nil:
+ cast[NSView](self).removeTrackingArea(window.trackingArea)
+ window.trackingArea.release()
+ window.trackingArea = nil
+
+ window.trackingArea = NSTrackingArea.alloc().initWithRect(
+ NSMakeRect(0, 0, 0, 0),
+ NSTrackingMouseEnteredAndExited or NSTrackingMouseMoved or
+ NSTrackingActiveInKeyWindow or NSTrackingCursorUpdate or
+ NSTrackingInVisibleRect or NSTrackingAssumeInside,
+ self,
+ cast[ID](nil),
+ )
+
+ cast[NSView](self).addTrackingArea(window.trackingArea)
+
+ callSuper(cast[NSObject](self), cmd)
+
+ addMethod "draggingEntered:",
+ proc(self: Id, cmd: Sel, sender: NSDraggingInfo): NSDragOperation {.cdecl.} =
+ getWindow(self)
+ if sender == nil:
+ return NSDragOperationNone
+
+ window.lastDragStatus = DragStatus.rejected
+ window.updateDragClipboard(sender.draggingPasteboard)
+ updateMousePos(
+ window, sender.draggingLocation, MouseMoveKind.moveWhileDragging
+ )
+ dragOperation(window.lastDragStatus)
+
+ addMethod "draggingUpdated:",
+ proc(self: Id, cmd: Sel, sender: NSDraggingInfo): NSDragOperation {.cdecl.} =
+ getWindow(self)
+ if sender == nil:
+ return NSDragOperationNone
+
+ window.updateDragClipboard(sender.draggingPasteboard)
+ updateMousePos(
+ window, sender.draggingLocation, MouseMoveKind.moveWhileDragging
+ )
+ dragOperation(window.lastDragStatus)
+
+ addMethod "draggingExited:",
+ proc(self: Id, cmd: Sel, sender: NSDraggingInfo): Id {.cdecl.} =
+ getWindow(self)
+ window.clearDragClipboard()
+ window.lastDragStatus = DragStatus.rejected
+
+ addMethod "performDragOperation:",
+ proc(self: Id, cmd: Sel, sender: NSDraggingInfo): bool {.cdecl.} =
+ getWindow(self)
+ if sender != nil:
+ window.updateDragClipboard(sender.draggingPasteboard)
+ if window.eventsHandler.onDrop != nil:
+ window.eventsHandler.onDrop(DropEvent(window: window))
+ let accepted = window.lastDragStatus == DragStatus.accepted
+ window.clearDragClipboard()
+ window.lastDragStatus = DragStatus.rejected
+ accepted
+
+ addMethod "mouseMoved:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ updateMousePos window, event.locationInWindow, MouseMoveKind.move
+
+ addMethod "mouseDragged:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ updateMousePos window, event.locationInWindow, MouseMoveKind.move
+
+ addMethod "rightMouseDragged:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ updateMousePos window, event.locationInWindow, MouseMoveKind.move
+
+ addMethod "otherMouseDragged:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ updateMousePos window, event.locationInWindow, MouseMoveKind.move
+
+ addMethod "scrollWheel:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ var
+ deltaX = event.scrollingDeltaX
+ deltaY = event.scrollingDeltaY
+
+ if event.hasPreciseScrollingDeltas:
+ deltaX *= 0.1
+ deltaY *= 0.1
+
+ if abs(deltaX) > 0 or abs(deltaY) > 0:
+ window.eventsHandler.pushEvent onScroll,
+ ScrollEvent(window: window, delta: deltaY, deltaX: deltaX)
+
+ addMethod "mouseDown:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ window.handleMouseButton(MouseButton.left, true, event.locationInWindow)
+
+ addMethod "mouseUp:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ window.handleMouseButton(MouseButton.left, false, event.locationInWindow)
+
+ addMethod "rightMouseDown:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ window.handleMouseButton(MouseButton.right, true, event.locationInWindow)
+
+ addMethod "rightMouseUp:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ window.handleMouseButton(MouseButton.right, false, event.locationInWindow)
+
+ addMethod "otherMouseDown:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ case event.buttonNumber
+ of 2:
+ window.handleMouseButton(MouseButton.middle, true, event.locationInWindow)
+ of 3:
+ window.handleMouseButton(
+ MouseButton.forward, true, event.locationInWindow
+ )
+ of 4:
+ window.handleMouseButton(
+ MouseButton.backward, true, event.locationInWindow
+ )
+ else:
+ discard
+
+ addMethod "otherMouseUp:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ case event.buttonNumber
+ of 2:
+ window.handleMouseButton(
+ MouseButton.middle, false, event.locationInWindow
+ )
+ of 3:
+ window.handleMouseButton(
+ MouseButton.forward, false, event.locationInWindow
+ )
+ of 4:
+ window.handleMouseButton(
+ MouseButton.backward, false, event.locationInWindow
+ )
+ else:
discard
+
+ addMethod "keyDown:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ let key = event.keyCode.keycodeToKey
+ let modifiers = window.updateModifiers(event)
+ if key != Key.unknown:
+ window.keyboard.pressed.incl key
+ window.eventsHandler.pushEvent onKey,
+ KeyEvent(
+ window: window,
+ key: key,
+ pressed: true,
+ repeated: event.isARepeat.bool,
+ modifiers: modifiers,
+ )
+
+ # Let key events handle navigation/editing control keys directly;
+ # routing them through NSTextInputClient can suppress following key presses.
+ let shouldRouteToInputContext =
+ key notin {
+ Key.backspace, Key.del, Key.left, Key.right, Key.up, Key.down, Key.home,
+ Key.End, Key.pageUp, Key.pageDown, Key.tab, Key.enter, Key.escape,
+ }
+ if window.eventsHandler.onTextInput != nil and shouldRouteToInputContext:
+ var handledByInputContext = false
+ let inputContext = cast[NSView](self).inputContext
+ if inputContext != nil:
+ handledByInputContext = inputContext.handleEvent(event).bool
+
+ # Fallback for view classes where AppKit does not route insertText
+ # through NSTextInputClient even though keyDown is delivered.
+ if not handledByInputContext and
+ (
+ modifiers *
+ {ModifierKey.control, ModifierKey.alt, ModifierKey.system}
+ ).len == 0:
+ for r in ($event.characters).runes:
+ if r.int >= 32 and r.int notin 0xf700 .. 0xf7ff:
+ window.eventsHandler.pushEvent onTextInput,
+ TextInputEvent(
+ window: window, text: $r, repeated: event.isARepeat.bool
+ )
+
+ addMethod "keyUp:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ getWindow(self)
+ let key = event.keyCode.keycodeToKey
+ let modifiers = window.updateModifiers(event)
+ if key != Key.unknown:
+ window.keyboard.pressed.excl key
+ window.eventsHandler.pushEvent onKey,
+ KeyEvent(
+ window: window,
+ key: key,
+ pressed: false,
+ repeated: false,
+ modifiers: modifiers,
+ ) # todo: handle repeated
+
+ addMethod "flagsChanged:",
+ proc(self: Id, cmd: Sel, event: NsEvent): Id {.cdecl.} =
+ #? wtf is this?!?
+ getWindow(self)
+ let key = event.keyCode.keycodeToKey
+ let modifiers = window.updateModifiers(event)
+ if key != Key.unknown:
+ if key in window.keyboard.pressed:
+ window.keyboard.pressed.excl key
+ window.eventsHandler.pushEvent onKey,
+ KeyEvent(
+ window: window,
+ key: key,
+ pressed: false,
+ repeated: false,
+ modifiers: modifiers,
+ ) # todo: handle repeated
+ else:
+ window.keyboard.pressed.incl key
+ window.eventsHandler.pushEvent onKey,
+ KeyEvent(
+ window: window,
+ key: key,
+ pressed: true,
+ repeated: false,
+ modifiers: modifiers,
+ ) # todo: handle repeated
+
+ addMethod "hasMarkedText",
+ proc(self: Id, cmd: Sel): bool {.cdecl.} =
+ getWindow(self)
+ window.markedText != nil
+
+ addMethod "markedRange",
+ proc(self: Id, cmd: Sel): NsRange {.cdecl.} =
+ getWindow(self)
+ if window.markedText != nil:
+ NSMakeRange(0, window.markedText.length)
+ else:
+ NSRangeEmpty
+
+ addMethod "selectedRange",
+ proc(self: Id, cmd: Sel): NsRange {.cdecl.} =
+ getWindow(self)
+ NSMakeRange(0, 0)
+
+ addMethod "setMarkedText:selectedRange:replacementRange:",
+ proc(
+ self: Id,
+ cmd: Sel,
+ obj: Id,
+ selectedRange: NsRange,
+ replacementRange: NsRange,
+ ): Id {.cdecl.} =
+ getWindow(self)
+ var characters: NSString
+ if cast[NSObject](obj).isKindOfClass(NSAttributedString):
+ characters = $(cast[NSAttributedString](obj).toNSString())
+ else:
+ characters = cast[NSString](obj)
+
+ if window.markedText != nil:
+ window.markedText.release()
+
+ window.markedText = NSString.stringWithString(characters).retain()
+
+ addMethod "unmarkText",
+ proc(self: Id, cmd: Sel): Id {.cdecl.} =
+ discard
+
+ addMethod "validAttributesForMarkedText",
+ proc(self: Id, cmd: Sel): NSArrayAbstract {.cdecl.} =
+ cast[NSArrayAbstract](nil)
+
+ addMethod "attributedSubstringForProposedRange:actualRange:",
+ proc(self: Id, cmd: Sel, range: NsRange, actualRange: NsRangePointer): ID =
+ discard
+
+ addMethod "insertText:replacementRange:",
+ proc(self: Id, cmd: Sel, obj: Id, replacementRange: NsRange): Id {.cdecl.} =
+ getWindow(self)
+ var characters: NSString
+ if cast[NSObject](obj).isKindOfClass(NSAttributedString):
+ characters = $cast[NSAttributedString](obj).toNSString()
else:
- ## todo
- # let
- # encodedPng = cursor.image.encodePng()
- # image = NSImage.alloc().initWithData(NSData.dataWithBytes(
- # encodedPng[0].unsafeAddr,
- # encodedPng.len
- # ))
- # hotspot = NSMakePoint(
- # window.state.cursor.hotspot.x.float,
- # window.state.cursor.hotspot.y.float
- # )
- # cursor = NSCursor.alloc().initWithImage(image, hotspot)
- # self.NSView.addCursorRect(self.NSView.bounds, cursor)
+ characters = cast[NSString](obj)
+
+ var range = NSMakeRange(0, characters.length.uint)
+ while range.length > 0:
+ var
+ codepoint: uint32
+ usedLength: uint
+ discard characters.getBytes(
+ codepoint.addr,
+ sizeof(codepoint).uint,
+ usedLength.addr,
+ NSUTF32StringEncoding,
+ 0.NSStringEncodingConversionOptions,
+ range,
+ range.addr,
+ )
+ if codepoint >= 0xf700 and codepoint <= 0xf7ff:
+ continue
+ window.eventsHandler.pushEvent onTextInput,
+ TextInputEvent(window: window, text: $Rune(codepoint))
+
+ if window.markedText != nil:
+ window.markedText.release()
+ window.markedText = nil
+
+ addMethod "characterIndexForPoint:",
+ proc(self: Id, cmd: Sel, point: NsPoint): uint {.cdecl.} =
+ NSNotFound.uint
+
+ addMethod "firstRectForCharacterRange:actualRange:",
+ proc(
+ self: Id, cmd: Sel, range: NsRange, actualRange: NsRangePointer
+ ): NsRect {.cdecl.} =
+ getWindow(self)
+ let contentRect = window.handle.contentRectForFrameRect(window.handle.frame)
+ NSMakeRect(
+ contentRect.origin.x + 0,
+ contentRect.origin.y + contentRect.size.height - 1 - 0,
+ 0,
+ 0,
+ )
+
+ addMethod "doCommandBySelector:",
+ proc(self: Id, cmd: Sel, selector: Sel): Id {.cdecl.} =
+ discard
+
+ addMethod "resetCursorRects",
+ proc(self: Id, cmd: Sel): Id {.cdecl.} =
+ getWindow(self)
+ autoreleasepool:
+ case window.m_cursor.kind
+ of builtin:
+ discard
+ else:
+ ## todo
+ # let
+ # encodedPng = cursor.image.encodePng()
+ # image = NSImage.alloc().initWithData(NSData.dataWithBytes(
+ # encodedPng[0].unsafeAddr,
+ # encodedPng.len
+ # ))
+ # hotspot = NSMakePoint(
+ # window.state.cursor.hotspot.x.float,
+ # window.state.cursor.hotspot.y.float
+ # )
+ # cursor = NSCursor.alloc().initWithImage(image, hotspot)
+ # self.NSView.addCursorRect(self.NSView.bounds, cursor)
addSiwinViewClass("SiwinViewSoftware", "NSImageView", softwareViewClass)
addSiwinViewClass("SiwinViewOpenGL", "NSOpenGLView", openglViewClass)
@@ -1520,7 +1701,6 @@ proc init =
NSApp.setDelegate(cast[NSObject](appDelegateClass.new))
NSApp.finishLaunching()
-
method firstStep*(window: WindowCocoa, makeVisible = true) =
if makeVisible:
window.visible = true
@@ -1529,10 +1709,7 @@ method firstStep*(window: WindowCocoa, makeVisible = true) =
autoreleasepool:
while true:
let event = NSApp.nextEventMatchingMask(
- NSEventMaskAny,
- NSDate.distantPast,
- NSDefaultRunLoopMode,
- true
+ NSEventMaskAny, NSDate.distantPast, NSDefaultRunLoopMode, true
)
if event == nil:
break
@@ -1546,16 +1723,12 @@ method firstStep*(window: WindowCocoa, makeVisible = true) =
if window.canBecomeKeyWindow:
window.handle.makeKeyAndOrderFront(cast[ID](nil))
-
method step*(window: WindowCocoa) =
proc pumpEvents(mode: NSRunLoopMode, firstUntilDate: NSDate): bool =
var first = true
while true:
let event = NSApp.nextEventMatchingMask(
- NSEventMaskAny,
- (if first: firstUntilDate else: NSDate.distantPast),
- mode,
- true
+ NSEventMaskAny, (if first: firstUntilDate else: NSDate.distantPast), mode, true
)
if event == nil:
break
@@ -1578,14 +1751,14 @@ method step*(window: WindowCocoa) =
window.refreshModifiers()
- window.eventsHandler.pushEvent onTick, TickEvent(window: window) # todo: lastTickTime
-
+ window.eventsHandler.pushEvent onTick, TickEvent(window: window) # todo: lastTickTime
+
if window.redrawRequested:
window.redrawRequested = false
if window of WindowCocoaSoftwareRendering:
window.WindowCocoaSoftwareRendering.resizeSoftwarePixelBuffer(window.m_size)
window.eventsHandler.pushEvent onRender, RenderEvent(window: window)
-
+
if window of WindowCocoaSoftwareRendering:
window.WindowCocoaSoftwareRendering.presentSoftwarePixelBuffer()
elif window of WindowCocoaOpengl:
@@ -1600,48 +1773,62 @@ method pixelBuffer*(window: WindowCocoaSoftwareRendering): PixelBuffer =
format: PixelBufferFormat.rgbx_32bit,
)
-
proc newSoftwareRenderingWindowCocoa*(
- size = ivec2(1280, 720),
- title = "",
- screen = defaultScreenCocoa(),
- resizable = true,
- fullscreen = false,
- frameless = false,
- transparent = false,
+ size = ivec2(1280, 720),
+ title = "",
+ screen = defaultScreenCocoa(),
+ resizable = true,
+ fullscreen = false,
+ frameless = false,
+ transparent = false,
): WindowCocoaSoftwareRendering =
new result
- result.initWindowCocoaSoftwareRendering(size, screen, fullscreen, frameless, transparent)
+ result.initWindowCocoaSoftwareRendering(
+ size, screen, fullscreen, frameless, transparent
+ )
result.title = title
- if not resizable: result.resizable = false
+ if not resizable:
+ result.resizable = false
+
+proc newPopupWindowCocoa*(
+ parent: WindowCocoa, placement: PopupPlacement, transparent = false, grab = true
+): WindowCocoaSoftwareRendering =
+ if parent == nil:
+ raise ValueError.newException("Popup windows require a parent window")
+ new result
+ result.initPopupWindowCocoaSoftwareRendering(parent, placement, transparent, grab)
proc newOpenglWindowCocoa*(
- size = ivec2(1280, 720),
- title = "",
- screen = defaultScreenCocoa(),
- resizable = true,
- fullscreen = false,
- frameless = false,
- transparent = false,
- vsync = false,
- msaa = 0'i32,
+ size = ivec2(1280, 720),
+ title = "",
+ screen = defaultScreenCocoa(),
+ resizable = true,
+ fullscreen = false,
+ frameless = false,
+ transparent = false,
+ vsync = false,
+ msaa = 0'i32,
): WindowCocoaOpengl =
new result
- result.initWindowCocoaOpengl(size, screen, fullscreen, frameless, transparent, vsync, msaa)
+ result.initWindowCocoaOpengl(
+ size, screen, fullscreen, frameless, transparent, vsync, msaa
+ )
result.title = title
- if not resizable: result.resizable = false
+ if not resizable:
+ result.resizable = false
proc newMetalWindowCocoa*(
- size = ivec2(1280, 720),
- title = "",
- screen = defaultScreenCocoa(),
- resizable = true,
- fullscreen = false,
- frameless = false,
- transparent = false,
+ size = ivec2(1280, 720),
+ title = "",
+ screen = defaultScreenCocoa(),
+ resizable = true,
+ fullscreen = false,
+ frameless = false,
+ transparent = false,
): WindowCocoaMetal =
new result
result.initWindowCocoaMetal(size, screen, fullscreen, frameless, transparent)
result.title = title
- if not resizable: result.resizable = false
+ if not resizable:
+ result.resizable = false
diff --git a/src/siwin/platforms/wayland/window.nim b/src/siwin/platforms/wayland/window.nim
index 7716b32..cbff14f 100644
--- a/src/siwin/platforms/wayland/window.nim
+++ b/src/siwin/platforms/wayland/window.nim
@@ -1,4 +1,6 @@
-import std/[times, importutils, strformat, options, tables, os, uri, sequtils, strutils, math]
+import
+ std/
+ [times, importutils, strformat, options, tables, os, uri, sequtils, strutils, math]
from std/posix import pipe, close, write, read
import pkg/[vmath]
import ../../[colorutils, siwindefs]
@@ -16,24 +18,26 @@ type
WindowWaylandKind* {.pure.} = enum
XdgSurface
+ PopupSurface
LayerSurface
-
+
WindowWayland* = ref WindowWaylandObj
WindowWaylandObj* = object of Window
globals: SiwinGlobalsWayland
surface: Wl_surface
xdgSurface: Xdg_surface
xdgToplevel: Xdg_toplevel
+ xdgPopup: Xdg_popup
serverDecoration: Zxdg_toplevel_decoration_v1
# will be nil if compositor doesn't support this protocol
plasmaSurface: Org_kde_plasma_surface
# will be nil if compositor doesn't support this protocol
-
+
layerShellSurface: Zwlr_layer_surface_v1
# will be nil if compositor doesn't support this protocol (eg. GNOME)
-
+
idleInhibitor: Zwp_idle_inhibitor_v1
libdecorFrame: LibdecorFrame
@@ -60,143 +64,144 @@ type
lastKeyRepeatedTime: Time
bufferScaleFactor: int32
fractionalScaleFactor: float32
+ popupRepositionToken: uint32
viewport: Wp_viewport
fractionalScaleObj: Wp_fractional_scale_v1
toplevelIcon: Xdg_toplevel_icon_v1
toplevelIconBuffers: seq[SharedBuffer]
-
+
ClipboardWayland* = ref object of Clipboard
globals: SiwinGlobalsWayland
userContent: ClipboardConvertableContent
dataSource: Wl_data_source
-
+
ClipboardWaylandDnd* = ref object of Clipboard
globals: SiwinGlobalsWayland
-
WindowWaylandSoftwareRendering* = ref WindowWaylandSoftwareRenderingObj
WindowWaylandSoftwareRenderingObj* = object of WindowWayland
buffer: SharedBuffer
oldBuffer: SharedBuffer
-
proc waylandKeyToKey(keycode: uint32): Key =
case global_xkb_state_unmodified.xkb_state_key_get_one_sym(keycode + 8)
- of XKB_KEY_shiftL: Key.lshift
- of XKB_KEY_shiftR: Key.rshift
- of XKB_KEY_controlL: Key.lcontrol
- of XKB_KEY_controlR: Key.rcontrol
- of XKB_KEY_altL: Key.lalt
- of XKB_KEY_altR: Key.ralt
- of XKB_KEY_superL: Key.lsystem
- of XKB_KEY_superR: Key.rsystem
- of XKB_KEY_menu: Key.menu
- of XKB_KEY_escape: Key.escape
- of XKB_KEY_semicolon: Key.semicolon
- of XKB_KEY_slash: Key.slash
- of XKB_KEY_equal: Key.equal
- of XKB_KEY_minus: Key.minus
- of XKB_KEY_bracketleft: Key.lbracket
+ of XKB_KEY_shiftL: Key.lshift
+ of XKB_KEY_shiftR: Key.rshift
+ of XKB_KEY_controlL: Key.lcontrol
+ of XKB_KEY_controlR: Key.rcontrol
+ of XKB_KEY_altL: Key.lalt
+ of XKB_KEY_altR: Key.ralt
+ of XKB_KEY_superL: Key.lsystem
+ of XKB_KEY_superR: Key.rsystem
+ of XKB_KEY_menu: Key.menu
+ of XKB_KEY_escape: Key.escape
+ of XKB_KEY_semicolon: Key.semicolon
+ of XKB_KEY_slash: Key.slash
+ of XKB_KEY_equal: Key.equal
+ of XKB_KEY_minus: Key.minus
+ of XKB_KEY_bracketleft: Key.lbracket
of XKB_KEY_bracketright: Key.rbracket
- of XKB_KEY_comma: Key.comma
- of XKB_KEY_period: Key.dot
- of XKB_KEY_apostrophe: Key.quote
- of XKB_KEY_backslash: Key.backslash
- of XKB_KEY_grave: Key.tilde
- of XKB_KEY_space: Key.space
- of XKB_KEY_return: Key.enter
- of XKB_KEY_kpEnter: Key.enter
- of XKB_KEY_backspace: Key.backspace
- of XKB_KEY_tab: Key.tab
- of XKB_KEY_prior: Key.page_up
- of XKB_KEY_next: Key.page_down
- of XKB_KEY_end: Key.End
- of XKB_KEY_home: Key.home
- of XKB_KEY_insert: Key.insert
- of XKB_KEY_delete: Key.del
- of XKB_KEY_kpAdd: Key.add
- of XKB_KEY_kpSubtract: Key.subtract
- of XKB_KEY_kpMultiply: Key.multiply
- of XKB_KEY_kpDivide: Key.divide
- of XKB_KEY_capsLock: Key.capsLock
- of XKB_KEY_numLock: Key.numLock
- of XKB_KEY_scrollLock: Key.scrollLock
- of XKB_KEY_print: Key.printScreen
- of XKB_KEY_kpSeparator: Key.npadDot
- of XKB_KEY_pause: Key.pause
- of XKB_KEY_f1: Key.f1
- of XKB_KEY_f2: Key.f2
- of XKB_KEY_f3: Key.f3
- of XKB_KEY_f4: Key.f4
- of XKB_KEY_f5: Key.f5
- of XKB_KEY_f6: Key.f6
- of XKB_KEY_f7: Key.f7
- of XKB_KEY_f8: Key.f8
- of XKB_KEY_f9: Key.f9
- of XKB_KEY_f10: Key.f10
- of XKB_KEY_f11: Key.f11
- of XKB_KEY_f12: Key.f12
- of XKB_KEY_f13: Key.f13
- of XKB_KEY_f14: Key.f14
- of XKB_KEY_f15: Key.f15
- of XKB_KEY_left: Key.left
- of XKB_KEY_right: Key.right
- of XKB_KEY_up: Key.up
- of XKB_KEY_down: Key.down
- of XKB_KEY_kpInsert: Key.npad0
- of XKB_KEY_kpEnd: Key.npad1
- of XKB_KEY_kpDown: Key.npad2
- of XKB_KEY_kpPagedown: Key.npad3
- of XKB_KEY_kpLeft: Key.npad4
- of XKB_KEY_kpBegin: Key.npad5
- of XKB_KEY_kpRight: Key.npad6
- of XKB_KEY_kpHome: Key.npad7
- of XKB_KEY_kpUp: Key.npad8
- of XKB_KEY_kpPageup: Key.npad9
- of XKB_KEY_a: Key.a
- of XKB_KEY_b: Key.b
- of XKB_KEY_c: Key.c
- of XKB_KEY_d: Key.d
- of XKB_KEY_e: Key.e
- of XKB_KEY_f: Key.f
- of XKB_KEY_g: Key.g
- of XKB_KEY_h: Key.h
- of XKB_KEY_i: Key.i
- of XKB_KEY_j: Key.j
- of XKB_KEY_k: Key.k
- of XKB_KEY_l: Key.l
- of XKB_KEY_m: Key.m
- of XKB_KEY_n: Key.n
- of XKB_KEY_o: Key.o
- of XKB_KEY_p: Key.p
- of XKB_KEY_q: Key.q
- of XKB_KEY_r: Key.r
- of XKB_KEY_s: Key.s
- of XKB_KEY_t: Key.t
- of XKB_KEY_u: Key.u
- of XKB_KEY_v: Key.v
- of XKB_KEY_w: Key.w
- of XKB_KEY_x: Key.x
- of XKB_KEY_y: Key.y
- of XKB_KEY_z: Key.z
- of XKB_KEY_0: Key.n0
- of XKB_KEY_1: Key.n1
- of XKB_KEY_2: Key.n2
- of XKB_KEY_3: Key.n3
- of XKB_KEY_4: Key.n4
- of XKB_KEY_5: Key.n5
- of XKB_KEY_6: Key.n6
- of XKB_KEY_7: Key.n7
- of XKB_KEY_8: Key.n8
- of XKB_KEY_9: Key.n9
- of XKB_KEY_ISO_Level3_Shift: Key.level3_shift
- of XKB_KEY_ISO_Level5_Shift: Key.level5_shift
- else: Key.unknown
+ of XKB_KEY_comma: Key.comma
+ of XKB_KEY_period: Key.dot
+ of XKB_KEY_apostrophe: Key.quote
+ of XKB_KEY_backslash: Key.backslash
+ of XKB_KEY_grave: Key.tilde
+ of XKB_KEY_space: Key.space
+ of XKB_KEY_return: Key.enter
+ of XKB_KEY_kpEnter: Key.enter
+ of XKB_KEY_backspace: Key.backspace
+ of XKB_KEY_tab: Key.tab
+ of XKB_KEY_prior: Key.page_up
+ of XKB_KEY_next: Key.page_down
+ of XKB_KEY_end: Key.End
+ of XKB_KEY_home: Key.home
+ of XKB_KEY_insert: Key.insert
+ of XKB_KEY_delete: Key.del
+ of XKB_KEY_kpAdd: Key.add
+ of XKB_KEY_kpSubtract: Key.subtract
+ of XKB_KEY_kpMultiply: Key.multiply
+ of XKB_KEY_kpDivide: Key.divide
+ of XKB_KEY_capsLock: Key.capsLock
+ of XKB_KEY_numLock: Key.numLock
+ of XKB_KEY_scrollLock: Key.scrollLock
+ of XKB_KEY_print: Key.printScreen
+ of XKB_KEY_kpSeparator: Key.npadDot
+ of XKB_KEY_pause: Key.pause
+ of XKB_KEY_f1: Key.f1
+ of XKB_KEY_f2: Key.f2
+ of XKB_KEY_f3: Key.f3
+ of XKB_KEY_f4: Key.f4
+ of XKB_KEY_f5: Key.f5
+ of XKB_KEY_f6: Key.f6
+ of XKB_KEY_f7: Key.f7
+ of XKB_KEY_f8: Key.f8
+ of XKB_KEY_f9: Key.f9
+ of XKB_KEY_f10: Key.f10
+ of XKB_KEY_f11: Key.f11
+ of XKB_KEY_f12: Key.f12
+ of XKB_KEY_f13: Key.f13
+ of XKB_KEY_f14: Key.f14
+ of XKB_KEY_f15: Key.f15
+ of XKB_KEY_left: Key.left
+ of XKB_KEY_right: Key.right
+ of XKB_KEY_up: Key.up
+ of XKB_KEY_down: Key.down
+ of XKB_KEY_kpInsert: Key.npad0
+ of XKB_KEY_kpEnd: Key.npad1
+ of XKB_KEY_kpDown: Key.npad2
+ of XKB_KEY_kpPagedown: Key.npad3
+ of XKB_KEY_kpLeft: Key.npad4
+ of XKB_KEY_kpBegin: Key.npad5
+ of XKB_KEY_kpRight: Key.npad6
+ of XKB_KEY_kpHome: Key.npad7
+ of XKB_KEY_kpUp: Key.npad8
+ of XKB_KEY_kpPageup: Key.npad9
+ of XKB_KEY_a: Key.a
+ of XKB_KEY_b: Key.b
+ of XKB_KEY_c: Key.c
+ of XKB_KEY_d: Key.d
+ of XKB_KEY_e: Key.e
+ of XKB_KEY_f: Key.f
+ of XKB_KEY_g: Key.g
+ of XKB_KEY_h: Key.h
+ of XKB_KEY_i: Key.i
+ of XKB_KEY_j: Key.j
+ of XKB_KEY_k: Key.k
+ of XKB_KEY_l: Key.l
+ of XKB_KEY_m: Key.m
+ of XKB_KEY_n: Key.n
+ of XKB_KEY_o: Key.o
+ of XKB_KEY_p: Key.p
+ of XKB_KEY_q: Key.q
+ of XKB_KEY_r: Key.r
+ of XKB_KEY_s: Key.s
+ of XKB_KEY_t: Key.t
+ of XKB_KEY_u: Key.u
+ of XKB_KEY_v: Key.v
+ of XKB_KEY_w: Key.w
+ of XKB_KEY_x: Key.x
+ of XKB_KEY_y: Key.y
+ of XKB_KEY_z: Key.z
+ of XKB_KEY_0: Key.n0
+ of XKB_KEY_1: Key.n1
+ of XKB_KEY_2: Key.n2
+ of XKB_KEY_3: Key.n3
+ of XKB_KEY_4: Key.n4
+ of XKB_KEY_5: Key.n5
+ of XKB_KEY_6: Key.n6
+ of XKB_KEY_7: Key.n7
+ of XKB_KEY_8: Key.n8
+ of XKB_KEY_9: Key.n9
+ of XKB_KEY_ISO_Level3_Shift: Key.level3_shift
+ of XKB_KEY_ISO_Level5_Shift: Key.level5_shift
+ else: Key.unknown
proc waylandKeyToString(keycode: uint32): string =
result = newStringOfCap(8)
result.setLen 1
- result.setLen global_xkb_state.xkb_state_key_get_utf8(keycode + 8, cast[cstring](result[0].addr), 7)
+ result.setLen global_xkb_state.xkb_state_key_get_utf8(
+ keycode + 8, cast[cstring](result[0].addr), 7
+ )
proc modifiersFromPressedKeys(keys: set[Key]): set[ModifierKey] =
if (keys * {Key.lshift, Key.rshift}).len != 0:
@@ -211,31 +216,49 @@ proc modifiersFromPressedKeys(keys: set[Key]): set[ModifierKey] =
proc refreshKeyboardModifiers(window: WindowWayland) =
var modifiers = modifiersFromPressedKeys(window.keyboard.pressed)
if global_xkb_state != nil:
- if global_xkb_state.xkb_state_mod_name_is_active("Shift".cstring, XKB_STATE_MODS_EFFECTIVE) != 0:
+ if global_xkb_state.xkb_state_mod_name_is_active(
+ "Shift".cstring, XKB_STATE_MODS_EFFECTIVE
+ ) != 0:
modifiers.incl ModifierKey.shift
- if global_xkb_state.xkb_state_mod_name_is_active("Control".cstring, XKB_STATE_MODS_EFFECTIVE) != 0:
+ if global_xkb_state.xkb_state_mod_name_is_active(
+ "Control".cstring, XKB_STATE_MODS_EFFECTIVE
+ ) != 0:
modifiers.incl ModifierKey.control
if (
- global_xkb_state.xkb_state_mod_name_is_active("Mod1".cstring, XKB_STATE_MODS_EFFECTIVE) != 0 or
- global_xkb_state.xkb_state_mod_name_is_active("Alt".cstring, XKB_STATE_MODS_EFFECTIVE) != 0
+ global_xkb_state.xkb_state_mod_name_is_active(
+ "Mod1".cstring, XKB_STATE_MODS_EFFECTIVE
+ ) != 0 or
+ global_xkb_state.xkb_state_mod_name_is_active(
+ "Alt".cstring, XKB_STATE_MODS_EFFECTIVE
+ ) != 0
):
modifiers.incl ModifierKey.alt
if (
- global_xkb_state.xkb_state_mod_name_is_active("Mod4".cstring, XKB_STATE_MODS_EFFECTIVE) != 0 or
- global_xkb_state.xkb_state_mod_name_is_active("Super".cstring, XKB_STATE_MODS_EFFECTIVE) != 0
+ global_xkb_state.xkb_state_mod_name_is_active(
+ "Mod4".cstring, XKB_STATE_MODS_EFFECTIVE
+ ) != 0 or
+ global_xkb_state.xkb_state_mod_name_is_active(
+ "Super".cstring, XKB_STATE_MODS_EFFECTIVE
+ ) != 0
):
modifiers.incl ModifierKey.system
- if global_xkb_state.xkb_state_mod_name_is_active("Lock".cstring, XKB_STATE_MODS_EFFECTIVE) != 0:
+ if global_xkb_state.xkb_state_mod_name_is_active(
+ "Lock".cstring, XKB_STATE_MODS_EFFECTIVE
+ ) != 0:
modifiers.incl ModifierKey.capsLock
if (
- global_xkb_state.xkb_state_mod_name_is_active("NumLock".cstring, XKB_STATE_MODS_EFFECTIVE) != 0 or
- global_xkb_state.xkb_state_mod_name_is_active("Mod2".cstring, XKB_STATE_MODS_EFFECTIVE) != 0
+ global_xkb_state.xkb_state_mod_name_is_active(
+ "NumLock".cstring, XKB_STATE_MODS_EFFECTIVE
+ ) != 0 or
+ global_xkb_state.xkb_state_mod_name_is_active(
+ "Mod2".cstring, XKB_STATE_MODS_EFFECTIVE
+ ) != 0
):
modifiers.incl ModifierKey.numLock
window.keyboard.modifiers = modifiers
-
-method swapBuffers(window: WindowWayland) {.base.} = discard
+method swapBuffers(window: WindowWayland) {.base.} =
+ discard
proc screenCountWayland*(globals: SiwinGlobalsWayland): int32 =
## todo
@@ -243,17 +266,21 @@ proc screenCountWayland*(globals: SiwinGlobalsWayland): int32 =
proc screenWayland*(globals: SiwinGlobalsWayland, number: int32): ScreenWayland =
new result
- if number notin 0.. 0'f32 and window.viewport != typeof(window.viewport).default
+ window.fractionalScaleFactor > 0'f32 and
+ window.viewport != typeof(window.viewport).default
proc effectiveUiScale(window: WindowWayland): float32 {.inline.} =
if window.hasFractionalScaling():
@@ -379,24 +405,28 @@ proc bufferScale(window: WindowWayland): int32 {.inline.} =
else:
max(1'i32, ceil(window.effectiveUiScale()).int32)
-proc scaledBufferLength(logical: int32; uiScale: float32): int32 {.inline.} =
+proc scaledBufferLength(logical: int32, uiScale: float32): int32 {.inline.} =
max(1'i32, ((logical.float32 * uiScale) + 0.5'f32).int32)
proc bufferSize(window: WindowWayland, logicalSize: IVec2): IVec2 {.inline.} =
let scale = window.effectiveUiScale()
- ivec2(scaledBufferLength(logicalSize.x, scale), scaledBufferLength(logicalSize.y, scale))
+ ivec2(
+ scaledBufferLength(logicalSize.x, scale), scaledBufferLength(logicalSize.y, scale)
+ )
-proc toLogicalLength(window: WindowWayland; backing: float32): float32 {.inline.} =
+proc toLogicalLength(window: WindowWayland, backing: float32): float32 {.inline.} =
let scale = window.effectiveUiScale()
if scale <= 1'f32:
backing
else:
backing / scale
-proc toLogicalLength(window: WindowWayland; backing: int32): int32 {.inline.} =
+proc toLogicalLength(window: WindowWayland, backing: int32): int32 {.inline.} =
max(1'i32, (window.toLogicalLength(backing.float32) + 0.5'f32).int32)
-proc reportedPointerPos(window: WindowWayland, surfaceX, surfaceY: float32): Vec2 {.inline.} =
+proc reportedPointerPos(
+ window: WindowWayland, surfaceX, surfaceY: float32
+): Vec2 {.inline.} =
let scale = window.effectiveUiScale()
if scale <= 1'f32:
vec2(surfaceX, surfaceY)
@@ -409,13 +439,74 @@ method uiScale*(window: WindowWayland): float32 =
method reportedSize*(window: WindowWayland): IVec2 =
window.bufferSize(window.m_size)
+proc popupAnchor(anchor: PopupAnchor): `Xdg_positioner / Anchor` =
+ case anchor
+ of PopupAnchor.paTopLeft: `Xdg_positioner / Anchor`.top_left
+ of PopupAnchor.paTop: `Xdg_positioner / Anchor`.top
+ of PopupAnchor.paTopRight: `Xdg_positioner / Anchor`.top_right
+ of PopupAnchor.paLeft: `Xdg_positioner / Anchor`.left
+ of PopupAnchor.paCenter: `Xdg_positioner / Anchor`.none
+ of PopupAnchor.paRight: `Xdg_positioner / Anchor`.right
+ of PopupAnchor.paBottomLeft: `Xdg_positioner / Anchor`.bottom_left
+ of PopupAnchor.paBottom: `Xdg_positioner / Anchor`.bottom
+ of PopupAnchor.paBottomRight: `Xdg_positioner / Anchor`.bottom_right
+
+proc popupGravity(gravity: PopupGravity): `Xdg_positioner / Gravity` =
+ case gravity
+ of PopupGravity.pgTopLeft: `Xdg_positioner / Gravity`.top_left
+ of PopupGravity.pgTop: `Xdg_positioner / Gravity`.top
+ of PopupGravity.pgTopRight: `Xdg_positioner / Gravity`.top_right
+ of PopupGravity.pgLeft: `Xdg_positioner / Gravity`.left
+ of PopupGravity.pgCenter: `Xdg_positioner / Gravity`.none
+ of PopupGravity.pgRight: `Xdg_positioner / Gravity`.right
+ of PopupGravity.pgBottomLeft: `Xdg_positioner / Gravity`.bottom_left
+ of PopupGravity.pgBottom: `Xdg_positioner / Gravity`.bottom
+ of PopupGravity.pgBottomRight: `Xdg_positioner / Gravity`.bottom_right
+
+proc popupConstraintAdjustment(adjustments: set[PopupConstraintAdjustment]): uint32 =
+ if PopupConstraintAdjustment.pcaSlideX in adjustments:
+ result = result or `Xdg_positioner / Constraint_adjustment`.slide_x.uint32
+ if PopupConstraintAdjustment.pcaSlideY in adjustments:
+ result = result or `Xdg_positioner / Constraint_adjustment`.slide_y.uint32
+ if PopupConstraintAdjustment.pcaFlipX in adjustments:
+ result = result or `Xdg_positioner / Constraint_adjustment`.flip_x.uint32
+ if PopupConstraintAdjustment.pcaFlipY in adjustments:
+ result = result or `Xdg_positioner / Constraint_adjustment`.flip_y.uint32
+ if PopupConstraintAdjustment.pcaResizeX in adjustments:
+ result = result or `Xdg_positioner / Constraint_adjustment`.resize_x.uint32
+ if PopupConstraintAdjustment.pcaResizeY in adjustments:
+ result = result or `Xdg_positioner / Constraint_adjustment`.resize_y.uint32
+
+proc newPositioner(window: WindowWayland, placement: PopupPlacement): Xdg_positioner =
+ result = window.globals.xdgWmBase.create_positioner()
+ let size = placement.popupSize()
+ result.set_size(size.x, size.y)
+ result.set_anchor_rect(
+ placement.anchorRectPos.x,
+ placement.anchorRectPos.y,
+ max(1, placement.anchorRectSize.x),
+ max(1, placement.anchorRectSize.y),
+ )
+ result.set_anchor(placement.anchor.popupAnchor())
+ result.set_gravity(placement.gravity.popupGravity())
+ result.set_constraint_adjustment(
+ placement.constraintAdjustment.popupConstraintAdjustment()
+ )
+ result.set_offset(placement.offset.x, placement.offset.y)
+ if placement.reactive:
+ result.set_reactive()
+ if result.proxy.wl_proxy_get_version() >= 3 and window.parentWindow() != nil:
+ let parent = window.parentWindow().WindowWayland
+ result.set_parent_size(parent.m_size.x, parent.m_size.y)
method close*(window: WindowWayland) =
+ if window.m_closed:
+ return
window.m_closed = true
+ window.notifyPopupDone(PopupDismissReason.pdrClientClosed)
window.eventsHandler.onClose.pushEvent CloseEvent(window: window)
release window
-
proc initClipboardsIfNeeded(globals: SiwinGlobalsWayland) =
if globals.primaryClipboard == nil:
globals.primaryClipboard = ClipboardWayland(globals: globals)
@@ -424,8 +515,7 @@ proc initClipboardsIfNeeded(globals: SiwinGlobalsWayland) =
if globals.dragndropClipboard == nil:
globals.dragndropClipboard = ClipboardWaylandDnd(globals: globals)
-
-proc basicInitWindow(window: WindowWayland; size: IVec2; screen: ScreenWayland) =
+proc basicInitWindow(window: WindowWayland, size: IVec2, screen: ScreenWayland) =
window.m_size = size
window.m_focused = false
window.m_resizable = true
@@ -439,7 +529,6 @@ proc basicInitWindow(window: WindowWayland; size: IVec2; screen: ScreenWayland)
window.m_selectionClipboard = window.globals.selectionClipboard
window.m_dragndropClipboard = window.globals.dragndropClipboard
-
method doResize(window: WindowWayland, size: IVec2) {.base.} =
window.m_size = size
@@ -452,7 +541,6 @@ method doResize(window: WindowWayland, size: IVec2) {.base.} =
window.surface.set_opaque_region(opaqueRegion)
destroy opaqueRegion
-
method doResize(window: WindowWaylandSoftwareRendering, size: IVec2) =
procCall window.WindowWayland.doResize(size)
let scaledSize = window.bufferSize(size)
@@ -461,7 +549,12 @@ method doResize(window: WindowWaylandSoftwareRendering, size: IVec2) =
swap window.buffer, window.oldBuffer
if window.buffer == nil:
- window.buffer = window.globals.create(window.globals.shm, scaledSize, (if window.m_transparent: argb8888 else: xrgb8888), bufferCount = 2)
+ window.buffer = window.globals.create(
+ window.globals.shm,
+ scaledSize,
+ (if window.m_transparent: argb8888 else: xrgb8888),
+ bufferCount = 2,
+ )
else:
try:
window.buffer.resize(scaledSize)
@@ -477,7 +570,6 @@ method doResize(window: WindowWaylandSoftwareRendering, size: IVec2) =
# no need to attach buffer yet
-
proc setFrameless(window: WindowWayland, v: bool)
# libdecor/xdg abstraction, prefixed with toplevel
@@ -499,7 +591,8 @@ proc toplevelForIcon(window: WindowWayland): Xdg_toplevel =
if rawToplevel == nil:
return typeof(result).default
- window.xdgToplevel = Xdg_toplevel(proxy: Wl_proxy(raw: cast[ptr Wl_object](rawToplevel)))
+ window.xdgToplevel =
+ Xdg_toplevel(proxy: Wl_proxy(raw: cast[ptr Wl_object](rawToplevel)))
window.xdgToplevel
proc toplevelSetTitle(window: WindowWayland, title: string) =
@@ -528,23 +621,31 @@ proc toplevelSetMaxSize(window: WindowWayland, x, y: int32) =
proc toplevelSetFullscreen(window: WindowWayland, v: bool) =
if window.useLibdecor:
- if v: libdecor_frame_set_fullscreen(window.libdecorFrame, nil)
- else: libdecor_frame_unset_fullscreen(window.libdecorFrame)
+ if v:
+ libdecor_frame_set_fullscreen(window.libdecorFrame, nil)
+ else:
+ libdecor_frame_unset_fullscreen(window.libdecorFrame)
else:
if v:
- if window.m_frameless: window.setFrameless(true)
+ if window.m_frameless:
+ window.setFrameless(true)
window.xdgToplevel.set_fullscreen(Wl_output(proxy: Wl_proxy(raw: nil)))
else:
window.xdgToplevel.unset_fullscreen()
- if not window.m_frameless: window.setFrameless(false)
+ if not window.m_frameless:
+ window.setFrameless(false)
proc toplevelSetMaximized(window: WindowWayland, v: bool) =
if window.useLibdecor:
- if v: libdecor_frame_set_maximized(window.libdecorFrame)
- else: libdecor_frame_unset_maximized(window.libdecorFrame)
+ if v:
+ libdecor_frame_set_maximized(window.libdecorFrame)
+ else:
+ libdecor_frame_unset_maximized(window.libdecorFrame)
else:
- if v: window.xdgToplevel.set_maximized()
- else: window.xdgToplevel.unsetMaximized()
+ if v:
+ window.xdgToplevel.set_maximized()
+ else:
+ window.xdgToplevel.unsetMaximized()
proc toplevelSetMinimized(window: WindowWayland) =
if window.useLibdecor:
@@ -558,15 +659,21 @@ proc toplevelMove(window: WindowWayland, serial: uint32) =
else:
window.xdgToplevel.move(window.globals.seat, serial)
-proc toplevelResize(window: WindowWayland, serial: uint32, edge: `Xdg_toplevel/Resize_edge`) =
+proc toplevelResize(
+ window: WindowWayland, serial: uint32, edge: `Xdg_toplevel / Resize_edge`
+) =
if window.useLibdecor:
- libdecor_frame_resize(window.libdecorFrame, window.globals.seat.proxy.raw, serial, edge.uint32)
+ libdecor_frame_resize(
+ window.libdecorFrame, window.globals.seat.proxy.raw, serial, edge.uint32
+ )
else:
window.xdgToplevel.resize(window.globals.seat, serial, edge)
proc toplevelShowWindowMenu(window: WindowWayland, serial: uint32, x, y: int32) =
if window.useLibdecor:
- libdecor_frame_show_window_menu(window.libdecorFrame, window.globals.seat.proxy.raw, serial, x.cint, y.cint)
+ libdecor_frame_show_window_menu(
+ window.libdecorFrame, window.globals.seat.proxy.raw, serial, x.cint, y.cint
+ )
else:
window.xdgToplevel.show_window_menu(window.globals.seat, serial, x, y)
@@ -574,7 +681,9 @@ proc resize(window: WindowWayland, size: IVec2)
proc createLibdecorFrameIface(): LibdecorFrameInterface =
LibdecorFrameInterface(
- configure: proc(frame: LibdecorFrame, cfg: LibdecorConfiguration, userData: pointer) {.cdecl.} =
+ configure: proc(
+ frame: LibdecorFrame, cfg: LibdecorConfiguration, userData: pointer
+ ) {.cdecl.} =
let win = cast[WindowWayland](userData)
var w, h: cint
if libdecor_configuration_get_content_size(cfg, frame, w.addr, h.addr):
@@ -590,9 +699,13 @@ proc createLibdecorFrameIface(): LibdecorFrameInterface =
let newVal = (winState and LibdecorWindowState.flag.uint32) != 0
if win.m != newVal:
win.m = newVal
- if win.opened: win.eventsHandler.onStateBoolChanged.pushEvent StateBoolChangedEvent(
- window: win, kind: StateBoolChangedEventKind.k, value: win.m, isExternal: true
- )
+ if win.opened:
+ win.eventsHandler.onStateBoolChanged.pushEvent StateBoolChangedEvent(
+ window: win,
+ kind: StateBoolChangedEventKind.k,
+ value: win.m,
+ isExternal: true,
+ )
handleState maximized, maximized, m_maximized
handleState fullscreen, fullscreen, m_fullscreen
@@ -605,13 +718,13 @@ proc createLibdecorFrameIface(): LibdecorFrameInterface =
,
close: proc(frame: LibdecorFrame, userData: pointer) {.cdecl.} =
let win = cast[WindowWayland](userData)
- win.m_closed = true
- ,
+ win.m_closed = true,
commit: proc(frame: LibdecorFrame, userData: pointer) {.cdecl.} =
let win = cast[WindowWayland](userData)
commit win.surface
,
- dismissPopup: nil)
+ dismissPopup: nil,
+ )
proc setupOpaqueRegion(window: WindowWayland, size: IVec2, transparent: bool) =
window.m_transparent = transparent
@@ -621,36 +734,47 @@ proc setupOpaqueRegion(window: WindowWayland, size: IVec2, transparent: bool) =
window.surface.set_opaque_region(opaqueRegion)
destroy opaqueRegion
-
proc resize(window: WindowWayland, size: IVec2) =
if size.x <= 0 or size.y <= 0:
## todo: means we should decide the size by ourselves
return
-
+
window.doResize size
-
+
case window.kind
of WindowWaylandKind.XdgSurface:
if not window.m_resizable:
window.toplevelSetMinSize(window.m_size.x, window.m_size.y)
window.toplevelSetMaxSize(window.m_size.x, window.m_size.y)
- window.eventsHandler.onResize.pushEvent ResizeEvent(window: window, size: window.size)
-
+ window.eventsHandler.onResize.pushEvent ResizeEvent(
+ window: window, size: window.size
+ )
+ of WindowWaylandKind.PopupSurface:
+ window.eventsHandler.onResize.pushEvent ResizeEvent(
+ window: window, size: window.size
+ )
+ window.redraw()
of WindowWaylandKind.LayerSurface:
window.layerShellSurface.set_size(window.m_size.x.uint32, window.m_size.y.uint32)
- window.eventsHandler.onResize.pushEvent ResizeEvent(window: window, size: window.size)
+ window.eventsHandler.onResize.pushEvent ResizeEvent(
+ window: window, size: window.size
+ )
window.redraw()
-
method `title=`*(window: WindowWayland, v: string) =
case window.kind
of WindowWaylandKind.XdgSurface:
window.toplevelSetTitle(v)
- else: discard
-
+ of WindowWaylandKind.PopupSurface:
+ discard
+ else:
+ discard
proc setFrameless(window: WindowWayland, v: bool) =
+ if window.kind != WindowWaylandKind.XdgSurface:
+ return
+
if window.useLibdecor:
if window.libdecorFrame != nil: # prevent something crazy after realease
libdecor_frame_set_visibility(window.libdecorFrame, not v)
@@ -658,41 +782,54 @@ proc setFrameless(window: WindowWayland, v: bool) =
if window.globals.serverDecorationManager != nil:
if window.serverDecoration == nil:
- window.serverDecoration = window.globals.serverDecorationManager.get_toplevel_decoration(window.xdg_toplevel)
-
+ window.serverDecoration = window.globals.serverDecorationManager.get_toplevel_decoration(
+ window.xdg_toplevel
+ )
+
window.serverDecoration.onConfigure:
- let newFrameless = case mode
- of client_side: true
- else: false
- if newFrameless == window.m_frameless: return
-
+ let newFrameless =
+ case mode
+ of client_side: true
+ else: false
+ if newFrameless == window.m_frameless:
+ return
+
window.m_frameless = newFrameless
window.eventsHandler.onStateBoolChanged.pushEvent StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.frameless, value: window.m_frameless, isExternal: true
+ window: window,
+ kind: StateBoolChangedEventKind.frameless,
+ value: window.m_frameless,
+ isExternal: true,
)
window.serverDecoration.set_mode:
- if v: client_side
- else: server_side
-
+ if v: client_side else: server_side
+
window.eventsHandler.onStateBoolChanged.pushEvent StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.frameless, value: window.m_frameless, isExternal: false
+ window: window,
+ kind: StateBoolChangedEventKind.frameless,
+ value: window.m_frameless,
+ isExternal: false,
)
-
method `fullscreen=`*(window: WindowWayland, v: bool) =
- if window.m_fullscreen == v: return
+ if window.kind != WindowWaylandKind.XdgSurface:
+ return
+ if window.m_fullscreen == v:
+ return
window.m_fullscreen = v
window.toplevelSetFullscreen(v)
-
method `frameless=`*(window: WindowWayland, v: bool) =
- if window.m_frameless == v: return
+ if window.m_frameless == v:
+ return
window.m_frameless = v
- if window.m_fullscreen: return # no system decorations needed for fullscreen windows
+ if window.kind != WindowWaylandKind.XdgSurface:
+ return
+ if window.m_fullscreen:
+ return # no system decorations needed for fullscreen windows
window.setFrameless(v)
-
method `size=`*(window: WindowWayland, v: IVec2) =
if window.fullscreen:
window.fullscreen = false
@@ -700,32 +837,65 @@ method `size=`*(window: WindowWayland, v: IVec2) =
if v.x <= 0 or v.y <= 0:
raise RangeDefect.newException("size must be > 0")
+ if window.kind == WindowWaylandKind.PopupSurface:
+ var placement = window.placement
+ placement.size = v
+ window.placement = placement
+ return
+
window.resize(v)
redraw window
-
method `pos=`*(window: WindowWayland, v: IVec2) =
- if window.m_pos == v: return
+ if window.kind == WindowWaylandKind.PopupSurface:
+ return
+ if window.m_pos == v:
+ return
window.m_pos = v
if window.fullscreen:
window.fullscreen = false
-
+
if window.plasmaSurface != nil:
window.plasmaSurface.set_position(v.x, v.y)
-
else:
# there are no protocol to force move window for Mutter (Gnome) and Weston compositors.
# there are zwlr_layer_shell_v1 for wlroots-based (and kde) compositors,
# but it doesnt seem to be the right protocol to use to move window
discard
-
+
# since no compositor notifies us about window movement, let's emulate such event
- if window.opened: window.eventsHandler.onWindowMove.pushEvent WindowMoveEvent(window: window, pos: v)
+ if window.opened:
+ window.eventsHandler.onWindowMove.pushEvent WindowMoveEvent(window: window, pos: v)
+method `placement=`*(window: WindowWayland, v: PopupPlacement) =
+ window.m_popupPlacement = v
+
+ if window.kind != WindowWaylandKind.PopupSurface:
+ let size = v.popupSize()
+ if window.m_size != size:
+ window.m_size = size
+ return
+
+ if window.xdgPopup != nil:
+ let positioner = window.newPositioner(v)
+ if window.popupRepositionToken == 0 or window.popupRepositionToken == high(uint32):
+ window.popupRepositionToken = 1
+ else:
+ inc window.popupRepositionToken
+ window.xdgPopup.reposition(positioner, window.popupRepositionToken)
+ destroy positioner
+ commit window.surface
+
+ window.resize(v.popupSize())
+
+method reposition*(window: WindowWayland, v: PopupPlacement) =
+ window.placement = v
method `cursor=`*(window: WindowWayland, v: Cursor) =
- if v.kind == builtin and window.m_cursor.kind == builtin and v.builtin == window.m_cursor.builtin: return
+ if v.kind == builtin and window.m_cursor.kind == builtin and
+ v.builtin == window.m_cursor.builtin:
+ return
## todo
proc applyToplevelIcon(window: WindowWayland, icon: Xdg_toplevel_icon_v1) =
@@ -740,7 +910,6 @@ proc applyToplevelIcon(window: WindowWayland, icon: Xdg_toplevel_icon_v1) =
manager.set_icon(toplevel, icon)
commit window.surface
-
method `icon=`*(window: WindowWayland, v: nil.typeof) =
clearToplevelIconResources(window)
window.applyToplevelIcon(typeof(window.toplevelIcon).default)
@@ -768,14 +937,17 @@ method `icon=`*(window: WindowWayland, v: PixelBuffer) =
var sourcePixels = newSeq[Color32bit](sourcePixelCount)
copyMem(sourcePixels[0].addr, v.data, sourcePixelCount * sizeof(Color32bit))
- var converted = PixelBuffer(data: sourcePixels[0].addr, size: v.size, format: v.format)
- convertPixelsInplace(converted.data, converted.size, v.format, PixelBufferFormat.bgra_32bit)
+ var converted =
+ PixelBuffer(data: sourcePixels[0].addr, size: v.size, format: v.format)
+ convertPixelsInplace(
+ converted.data, converted.size, v.format, PixelBufferFormat.bgra_32bit
+ )
var squarePixels = newSeq[Color32bit](iconSize.int * iconSize.int)
let rowBytes = v.size.x.int * sizeof(Color32bit)
let offsetX = ((iconSize - v.size.x) div 2).int
let offsetY = ((iconSize - v.size.y) div 2).int
- for y in 0.. 0 and delay >= 0:
globals.seat_keyboard_repeatSettings = (rate: rate, delay: delay)
-
if globals.tabletManager != nil:
globals.seat_tablet = globals.tabletManager.get_tablet_seat(globals.seat)
-
+
globals.seat_tablet.onTool_added:
# note: here nim closures ability to capture variables inside function scope is used
# todo: change wayland callbacks to {.nimcall.} instead of {.closure.} and explicitly handle captures
inc globals.lastTouchId
let touchId = globals.lastTouchId
let tool = construct(
- id.proxy.raw, globals.interfaces.addr,
- Zwp_tablet_tool_v2, `Zwp_tablet_tool_v2 / dispatch`, `Zwp_tablet_tool_v2 / Callbacks`
+ id.proxy.raw, globals.interfaces.addr, Zwp_tablet_tool_v2,
+ `Zwp_tablet_tool_v2 / dispatch`, `Zwp_tablet_tool_v2 / Callbacks`,
)
var currentWindow: WindowWayland = nil
var touch = Touch(id: touchId, device: TouchDeviceKind.graphicsTablet)
@@ -1226,63 +1468,74 @@ proc initSeatEvents*(globals: SiwinGlobalsWayland) =
destroy tool
tool.onProximity_in:
- if surface == nil: return
+ if surface == nil:
+ return
let window = globals.associatedWindows.getOrDefault(surface.proxy.raw.id, nil).WindowWayland
- if window == nil: return
+ if window == nil:
+ return
currentWindow = window
-
+
window.touchScreen.touches[touchId] = touch
window.enterSerial = serial
globals.lastSeatEventSerial = serial
replicateWindowTitleAndBorderBehaviour(window, window.mouse.pos)
- if window.opened: window.eventsHandler.onTouchMove.pushEvent TouchMoveEvent(
- window: window, touch: touch, pos: touch.pos, kind: MouseMoveKind.enter
- )
-
+ if window.opened:
+ window.eventsHandler.onTouchMove.pushEvent TouchMoveEvent(
+ window: window, touch: touch, pos: touch.pos, kind: MouseMoveKind.enter
+ )
+
tool.onProximity_out:
if currentWindow != nil:
- if currentWindow.opened: currentWindow.eventsHandler.onTouchMove.pushEvent TouchMoveEvent(
- window: currentWindow, touch: touch, pos: touch.pos, kind: MouseMoveKind.leave
- )
+ if currentWindow.opened:
+ currentWindow.eventsHandler.onTouchMove.pushEvent TouchMoveEvent(
+ window: currentWindow,
+ touch: touch,
+ pos: touch.pos,
+ kind: MouseMoveKind.leave,
+ )
currentWindow = nil
-
+
tool.onMotion:
touch.pos = vec2(x, y)
if currentWindow != nil:
- if currentWindow.opened: currentWindow.eventsHandler.onTouchMove.pushEvent TouchMoveEvent(
- window: currentWindow, touch: touch, pos: touch.pos, kind: MouseMoveKind.move
- )
-
+ if currentWindow.opened:
+ currentWindow.eventsHandler.onTouchMove.pushEvent TouchMoveEvent(
+ window: currentWindow,
+ touch: touch,
+ pos: touch.pos,
+ kind: MouseMoveKind.move,
+ )
+
tool.onDown:
touch.pressed = true
globals.lastSeatEventSerial = serial
if currentWindow != nil:
- if currentWindow.opened: currentWindow.eventsHandler.onTouch.pushEvent TouchEvent(
- window: currentWindow, touch: touch, pressed: touch.pressed
- )
-
+ if currentWindow.opened:
+ currentWindow.eventsHandler.onTouch.pushEvent TouchEvent(
+ window: currentWindow, touch: touch, pressed: touch.pressed
+ )
+
tool.onUp:
touch.pressed = false
if currentWindow != nil:
- if currentWindow.opened: currentWindow.eventsHandler.onTouch.pushEvent TouchEvent(
- window: currentWindow, touch: touch, pressed: touch.pressed
- )
-
+ if currentWindow.opened:
+ currentWindow.eventsHandler.onTouch.pushEvent TouchEvent(
+ window: currentWindow, touch: touch, pressed: touch.pressed
+ )
+
tool.onPressure:
touch.pressure = pressure.int / 65535
if currentWindow != nil:
- if currentWindow.opened: currentWindow.eventsHandler.onTouchPressureChanged.pushEvent TouchPressureChangedEvent(
- window: currentWindow, touch: touch, pressure: touch.pressure
- )
-
-
-
+ if currentWindow.opened:
+ currentWindow.eventsHandler.onTouchPressureChanged.pushEvent TouchPressureChangedEvent(
+ window: currentWindow, touch: touch, pressure: touch.pressure
+ )
proc setIdleInhibit*(window: WindowWayland, state: bool) =
#? should the proc be named `idleInhibit=`?
@@ -1292,24 +1545,33 @@ proc setIdleInhibit*(window: WindowWayland, state: bool) =
if state:
if window.idleInhibitor != nil:
#? should we return without an error here instead?
- raise newException(ValueError, "`setIdleInhibit(true)` was called even though this window already has an active inhibitor")
+ raise newException(
+ ValueError,
+ "`setIdleInhibit(true)` was called even though this window already has an active inhibitor",
+ )
- window.idleInhibitor = window.globals.idleInhibitManager.create_inhibitor(window.surface)
+ window.idleInhibitor =
+ window.globals.idleInhibitManager.create_inhibitor(window.surface)
else:
if window.idleInhibitor == nil:
#? should we return without an error here instead?
- raise newException(ValueError, "`setIdleInhibit(false)` was called even though the window has no active inhibitor")
-
+ raise newException(
+ ValueError,
+ "`setIdleInhibit(false)` was called even though the window has no active inhibitor",
+ )
+
window.idleInhibitor.destroy()
window.idleInhibitor.proxy.raw = nil
-
proc initDataDeviceManagerEvents*(globals: SiwinGlobalsWayland) =
- if globals.dataDeviceManagerEventsInitialized: return
+ if globals.dataDeviceManagerEventsInitialized:
+ return
globals.dataDeviceManagerEventsInitialized = true
- if globals.dataDeviceManager == nil: return
- if globals.seat == nil: return
+ if globals.dataDeviceManager == nil:
+ return
+ if globals.seat == nil:
+ return
globals.data_device = globals.dataDeviceManager.get_data_device(globals.seat)
@@ -1319,7 +1581,8 @@ proc initDataDeviceManagerEvents*(globals: SiwinGlobalsWayland) =
globals.unindentified_data_offer_mimeTypes = @[]
globals.unindentified_data_offer = id.proxy.raw.construct(
- globals.interfaces.`iface Wl_data_offer`.addr, Wl_data_offer, `Wl_data_offer/dispatch`, `Wl_data_offer/Callbacks`
+ globals.interfaces.`iface Wl_data_offer`.addr, Wl_data_offer,
+ `Wl_data_offer / dispatch`, `Wl_data_offer / Callbacks`,
)
globals.unindentified_data_offer.onOffer:
@@ -1328,15 +1591,15 @@ proc initDataDeviceManagerEvents*(globals: SiwinGlobalsWayland) =
globals.data_device.onSelection:
if globals.current_selection_data_offer != nil:
destroy globals.current_selection_data_offer
-
+
globals.current_selection_data_offer = globals.unindentified_data_offer
var offered_mime_types = globals.unindentified_data_offer_mimeTypes
-
+
globals.unindentified_data_offer.proxy.raw = nil
globals.unindentified_data_offer_mimeTypes = @[]
-
+
globals.initClipboardsIfNeeded()
-
+
globals.primaryClipboard.availableKinds = {}
globals.primaryClipboard.availableMimeTypes = @[]
@@ -1345,11 +1608,12 @@ proc initDataDeviceManagerEvents*(globals: SiwinGlobalsWayland) =
globals.current_selection_data_offer.onOffer:
offered_mime_types.add $mime_type
-
- discard wl_display_roundtrip globals.display # get all the mime types
-
+
+ discard wl_display_roundtrip globals.display # get all the mime types
+
for mime_type in offered_mime_types:
- if mime_type in ["UTF8_STRING", "STRING", "TEXT", "text/plain", "text/plain;charset=utf-8"]:
+ if mime_type in
+ ["UTF8_STRING", "STRING", "TEXT", "text/plain", "text/plain;charset=utf-8"]:
globals.primaryClipboard.availableKinds.incl ClipboardContentKind.text
if mime_type in ["text/uri-list"]:
@@ -1366,8 +1630,12 @@ proc initDataDeviceManagerEvents*(globals: SiwinGlobalsWayland) =
discard wl_display_roundtrip globals.display
-
-proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool, size: IVec2, class: string) =
+proc setupWindow(
+ window: WindowWayland,
+ fullscreen, frameless, transparent: bool,
+ size: IVec2,
+ class: string,
+) =
const FractionalScaleDenominator = 120'f32
proc applySurfaceScale(window: WindowWayland) =
@@ -1377,21 +1645,24 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
if window.m_size.x > 0 and window.m_size.y > 0:
window.doResize(window.m_size)
if window.opened:
- window.eventsHandler.onResize.pushEvent ResizeEvent(window: window, size: window.size)
+ window.eventsHandler.onResize.pushEvent ResizeEvent(
+ window: window, size: window.size
+ )
window.redraw()
expectExtension window.globals.compositor
expectExtension window.globals.xdgWmBase
-
+
window.globals.initSeatEvents()
-
+
window.surface = window.globals.compositor.create_surface
window.globals.associatedWindows[window.surface.proxy.raw.id] = window
if window.globals.viewporter.proxy != nil:
window.viewport = window.globals.viewporter.get_viewport(window.surface)
window.viewport.set_destination(size.x, size.y)
if window.globals.fractionalScaleManager.proxy != nil:
- window.fractionalScaleObj = window.globals.fractionalScaleManager.get_fractional_scale(window.surface)
+ window.fractionalScaleObj =
+ window.globals.fractionalScaleManager.get_fractional_scale(window.surface)
window.fractionalScaleObj.onPreferred_scale:
let newScale = max(1'f32, scale.float32 / FractionalScaleDenominator)
if abs(window.fractionalScaleFactor - newScale) < 0.0001'f32:
@@ -1408,17 +1679,19 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
case window.kind
of WindowWaylandKind.XdgSurface:
- let wantLibdecor = not frameless and
- window.globals.serverDecorationManager == nil and
- libdecorAvailable()
+ let wantLibdecor =
+ not frameless and window.globals.serverDecorationManager == nil and
+ libdecorAvailable()
if wantLibdecor:
- when defined(siwin_debug_echoLibdecor): echo "siwin: using libdecor for window decorations (server-side decorations unavailable)"
+ when defined(siwin_debug_echoLibdecor):
+ echo "siwin: using libdecor for window decorations (server-side decorations unavailable)"
window.useLibdecor = true
window.globals.initLibdecor()
if window.globals.libdecorCtx == nil:
- when defined(siwin_debug_echoLibdecor): echo "siwin: libdecor context creation failed, falling back to frameless"
+ when defined(siwin_debug_echoLibdecor):
+ echo "siwin: libdecor context creation failed, falling back to frameless"
window.useLibdecor = false
if window.useLibdecor:
@@ -1429,11 +1702,12 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
window.globals.libdecorCtx,
window.surface.proxy.raw,
window.libdecorFrameIface.addr,
- cast[pointer](window)
+ cast[pointer](window),
)
if window.libdecorFrame == nil:
- when defined(siwin_debug_echoLibdecor): echo "siwin: libdecor_decorate failed, falling back to frameless"
+ when defined(siwin_debug_echoLibdecor):
+ echo "siwin: libdecor_decorate failed, falling back to frameless"
GC_unref(window)
window.useLibdecor = false
@@ -1452,7 +1726,6 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
# dispatch libdecor to process initial configure
discard libdecor_dispatch(window.globals.libdecorCtx, 0)
-
else:
# Standard xdg path (no libdecor)
window.xdgSurface = window.globals.xdgWmBase.get_xdg_surface(window.surface)
@@ -1462,8 +1735,12 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
window.xdgSurface.ack_configure(serial)
redraw window
- window.fullscreen = fullscreen
+ # Force the initial decoration preference to be applied before the first
+ # surface commit. Without this, frameless Wayland windows can map once
+ # with compositor decorations because the setter short-circuits.
+ window.m_frameless = not frameless
window.frameless = frameless
+ window.fullscreen = fullscreen
window.setupOpaqueRegion(size, transparent)
@@ -1478,13 +1755,19 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
let states = states.toSeq(`XdgToplevel / State`)
- template checkState(state: `XdgToplevel / State`): bool = state in states
+ template checkState(state: `XdgToplevel / State`): bool =
+ state in states
+
template handleState(k, n, m: untyped) =
if window.m != checkState(`XdgToplevel / State`.n):
window.m = checkState(`XdgToplevel / State`.n)
- if window.opened: window.eventsHandler.onStateBoolChanged.pushEvent StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.k, value: window.m, isExternal: true
- )
+ if window.opened:
+ window.eventsHandler.onStateBoolChanged.pushEvent StateBoolChangedEvent(
+ window: window,
+ kind: StateBoolChangedEventKind.k,
+ value: window.m,
+ isExternal: true,
+ )
handleState maximized, maximized, m_maximized
handleState fullscreen, fullscreen, m_fullscreen
@@ -1492,13 +1775,48 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
if window.globals.plasmaShell != nil:
window.plasmaSurface = window.globals.plasmaShell.get_surface(window.surface)
+ of PopupSurface:
+ let parent = window.parentWindow().WindowWayland
+ if parent == nil:
+ raise ValueError.newException("Wayland popup windows require a parent window")
+ if parent.globals != window.globals:
+ raise ValueError.newException(
+ "Wayland popup parent must belong to the same globals/display"
+ )
+ if parent.xdgSurface == nil:
+ raise ValueError.newException("Wayland popup parent must expose an xdg_surface")
+
+ window.xdgSurface = window.globals.xdgWmBase.get_xdg_surface(window.surface)
+ let positioner = window.newPositioner(window.placement())
+ window.xdgPopup = window.xdgSurface.get_popup(parent.xdgSurface, positioner)
+ destroy positioner
+
+ window.xdgSurface.onConfigure:
+ window.xdgSurface.ack_configure(serial)
+ redraw window
+
+ window.xdgPopup.onConfigure:
+ if width > 0 and height > 0:
+ let configuredSize = ivec2(width, height)
+ if configuredSize != window.m_size:
+ window.resize(configuredSize)
+ redraw window
+
+ window.xdgPopup.onPopup_done:
+ window.notifyPopupDone(PopupDismissReason.pdrCompositorDismissed)
+ window.m_closed = true
+
+ if window.popupGrab and window.globals.seat != nil and
+ window.globals.lastSeatEventSerial != 0:
+ window.xdgPopup.grab(window.globals.seat, window.globals.lastSeatEventSerial)
+ window.setupOpaqueRegion(size, transparent)
of LayerSurface:
window.layerShellSurface = window.globals.layerShell.get_layer_surface(
window.surface,
Wl_output(proxy: Wl_proxy(raw: nil)),
- `Zwlr_layer_shell_v1/Layer`(window.layer.int),
- window.namespace.cstring
+ `Zwlr_layer_shell_v1 / Layer`(window.layer.int),
+ window.namespace.cstring,
)
window.layerShellSurface.set_size(window.m_size.x.uint32, window.m_size.y.uint32)
window.redraw()
@@ -1511,62 +1829,79 @@ proc setupWindow(window: WindowWayland, fullscreen, frameless, transparent: bool
window.m_closed = true
window.surface.destroy()
-
proc initSoftwareRenderingWindow(
- window: WindowWaylandSoftwareRendering,
- size: IVec2, screen: ScreenWayland,
- fullscreen, frameless, transparent: bool, class: string
+ window: WindowWaylandSoftwareRendering,
+ size: IVec2,
+ screen: ScreenWayland,
+ fullscreen, frameless, transparent: bool,
+ class: string,
) =
expectExtension window.globals.shm
window.basicInitWindow size, screen
-
+
window.setupWindow fullscreen, frameless, transparent, size, class
- window.buffer = window.globals.create(window.globals.shm, window.bufferSize(size), (if transparent: argb8888 else: xrgb8888), bufferCount = 2)
+ window.buffer = window.globals.create(
+ window.globals.shm,
+ window.bufferSize(size),
+ (if transparent: argb8888 else: xrgb8888),
+ bufferCount = 2,
+ )
+
+proc initPopupWindow(
+ window: WindowWaylandSoftwareRendering,
+ parent: WindowWayland,
+ placement: PopupPlacement,
+ grab: bool,
+ transparent: bool,
+) =
+ expectExtension window.globals.shm
+ window.initPopupState(parent, placement, grab)
+ window.basicInitWindow(placement.popupSize(), ScreenWayland(id: 0.cint))
+ window.kind = WindowWaylandKind.PopupSurface
+ window.setupWindow(false, true, transparent, placement.popupSize(), "")
+ window.buffer = window.globals.create(
+ window.globals.shm,
+ window.bufferSize(window.m_size),
+ (if transparent: argb8888 else: xrgb8888),
+ bufferCount = 2,
+ )
proc setAnchor*(window: WindowWayland, edge: LayerEdge | seq[LayerEdge]) =
if window.layerShellSurface == nil:
raise newException(
ValueError,
"Attempt to set surface anchor when layer shell surface hasn't been initialized." &
- "\nHint: Pass `kind` as `WindowWaylandKind.LayerSurface` when constructing this window."
+ "\nHint: Pass `kind` as `WindowWaylandKind.LayerSurface` when constructing this window.",
)
-
+
when edge is LayerEdge:
window.layerShellSurface.set_anchor(
case edge
- of LayerEdge.Top:
- `Zwlr_layer_surface_v1/Anchor`.top
- of LayerEdge.Left:
- `Zwlr_layer_surface_v1/Anchor`.left
- of LayerEdge.Right:
- `Zwlr_layer_surface_v1/Anchor`.right
- of LayerEdge.Bottom:
- `Zwlr_layer_surface_v1/Anchor`.bottom
+ of LayerEdge.Top: `Zwlr_layer_surface_v1 / Anchor`.top
+ of LayerEdge.Left: `Zwlr_layer_surface_v1 / Anchor`.left
+ of LayerEdge.Right: `Zwlr_layer_surface_v1 / Anchor`.right
+ of LayerEdge.Bottom: `Zwlr_layer_surface_v1 / Anchor`.bottom
)
else:
if edge.len < 2:
raise newException(ValueError, "Not enough edges provided")
- func convert(x: LayerEdge): `Zwlr_layer_surface_v1/Anchor` {.inline.} =
+ func convert(x: LayerEdge): `Zwlr_layer_surface_v1 / Anchor` {.inline.} =
case x
- of LayerEdge.Top:
- `Zwlr_layer_surface_v1/Anchor`.top
- of LayerEdge.Left:
- `Zwlr_layer_surface_v1/Anchor`.left
- of LayerEdge.Right:
- `Zwlr_layer_surface_v1/Anchor`.right
- of LayerEdge.Bottom:
- `Zwlr_layer_surface_v1/Anchor`.bottom
+ of LayerEdge.Top: `Zwlr_layer_surface_v1 / Anchor`.top
+ of LayerEdge.Left: `Zwlr_layer_surface_v1 / Anchor`.left
+ of LayerEdge.Right: `Zwlr_layer_surface_v1 / Anchor`.right
+ of LayerEdge.Bottom: `Zwlr_layer_surface_v1 / Anchor`.bottom
var final = edge[0].uint
for val in edge[1 ..< edge.len]:
final = final or val.uint
- window.layerShellSurface.set_anchor(cast[`Zwlr_layer_surface_v1/Anchor`](final))
+ window.layerShellSurface.set_anchor(cast[`Zwlr_layer_surface_v1 / Anchor`](final))
window.redraw()
@@ -1575,74 +1910,67 @@ proc setKeyboardInteractivity*(window: WindowWayland, mode: LayerInteractivityMo
raise newException(
ValueError,
"Attempt to set keyboard interactivity when layer shell surface hasn't been initialized." &
- "\nHint: Pass `kind` as `WindowWaylandKind.LayerSurface` when constructing this window."
+ "\nHint: Pass `kind` as `WindowWaylandKind.LayerSurface` when constructing this window.",
)
window.layerShellSurface.set_keyboard_interactivity(
case mode
of LayerInteractivityMode.None:
- `Zwlr_layer_surface_v1/Keyboard_interactivity`.none
+ `Zwlr_layer_surface_v1 / Keyboard_interactivity`.none
of LayerInteractivityMode.Exclusive:
- `Zwlr_layer_surface_v1/Keyboard_interactivity`.exclusive
+ `Zwlr_layer_surface_v1 / Keyboard_interactivity`.exclusive
of LayerInteractivityMode.OnDemand:
- `Zwlr_layer_surface_v1/Keyboard_interactivity`.on_demand
+ `Zwlr_layer_surface_v1 / Keyboard_interactivity`.on_demand
)
-
proc setExclusiveZone*(window: WindowWayland, zone: int32) =
if window.layerShellSurface == nil:
raise newException(
ValueError,
"Attempt to set keyboard interactivity when layer shell surface hasn't been initialized." &
- "\nHint: Pass `kind` as `WindowWaylandKind.LayerSurface` when constructing this window."
+ "\nHint: Pass `kind` as `WindowWaylandKind.LayerSurface` when constructing this window.",
)
window.layerShellSurface.set_exclusive_zone(zone)
-
proc constructClipboardContent*(
- data: sink string, kind: ClipboardContentKind, mimeType: string
+ data: sink string, kind: ClipboardContentKind, mimeType: string
): ClipboardContent =
case kind
of ClipboardContentKind.text:
result = ClipboardContent(kind: ClipboardContentKind.text, text: data)
-
of ClipboardContentKind.files:
let uris = data.splitLines
var files: seq[string]
-
+
for uri in uris:
let uri = parseUri(uri)
if uri.scheme == "file":
files.add uri.path.decodeUrl
-
+
result = ClipboardContent(kind: ClipboardContentKind.files, files: files)
-
of ClipboardContentKind.other:
- result = ClipboardContent(kind: ClipboardContentKind.other, mimeType: mimeType, data: data)
+ result =
+ ClipboardContent(kind: ClipboardContentKind.other, mimeType: mimeType, data: data)
-
-proc toString*(
- content: ClipboardConvertableContent, targetType: string
-): string =
+proc toString*(content: ClipboardConvertableContent, targetType: string): string =
var conv: ClipboardContentConverter
for cv in content.converters:
case cv.kind
of ClipboardContentKind.text:
- if targetType in ["UTF8_STRING", "STRING", "TEXT", "text/plain", "text/plain;charset=utf-8"]:
+ if targetType in
+ ["UTF8_STRING", "STRING", "TEXT", "text/plain", "text/plain;charset=utf-8"]:
conv = cv
break
-
of ClipboardContentKind.files:
if targetType in ["text/uri-list"]:
conv = cv
break
-
of ClipboardContentKind.other:
if targetType == cv.mimeType:
conv = cv
break
-
+
if conv.f == nil:
return ""
@@ -1651,49 +1979,53 @@ proc toString*(
case conv.kind
of ClipboardContentKind.text:
result = content.text
-
of ClipboardContentKind.files:
- result = content.files.mapIt($Uri(scheme: "file", path: it.encodeUrl(usePlus=false))).join("\n")
-
+ result = content.files
+ .mapIt($Uri(scheme: "file", path: it.encodeUrl(usePlus = false)))
+ .join("\n")
of ClipboardContentKind.other:
result = content.data
-
-
method content*(
- clipboard: ClipboardWayland, kind: ClipboardContentKind, mimeType: string = "text/plain"
+ clipboard: ClipboardWayland,
+ kind: ClipboardContentKind,
+ mimeType: string = "text/plain",
): ClipboardContent =
clipboard.globals.initDataDeviceManagerEvents()
-
+
var mimeType =
case kind
of ClipboardContentKind.text:
- if "text/plain;charset=utf-8" in clipboard.availableMimeTypes: "text/plain;charset=utf-8"
- elif "UTF8_STRING" in clipboard.availableMimeTypes: "UTF8_STRING"
- elif "STRING" in clipboard.availableMimeTypes: "STRING"
- elif "TEXT" in clipboard.availableMimeTypes: "TEXT"
- else: "text/plain"
-
+ if "text/plain;charset=utf-8" in clipboard.availableMimeTypes:
+ "text/plain;charset=utf-8"
+ elif "UTF8_STRING" in clipboard.availableMimeTypes:
+ "UTF8_STRING"
+ elif "STRING" in clipboard.availableMimeTypes:
+ "STRING"
+ elif "TEXT" in clipboard.availableMimeTypes:
+ "TEXT"
+ else:
+ "text/plain"
of ClipboardContentKind.files:
"text/uri-list"
-
of ClipboardContentKind.other:
mimeType
if mimeType notin clipboard.availableMimeTypes:
return constructClipboardContent("", kind, mimeType)
-
if clipboard == clipboard.globals.primaryClipboard.ClipboardWayland:
if clipboard.globals.current_selection_data_offer == nil:
# Compositors are not required to always send us a fresh selection offer
# for our own clipboard content. Fall back to local converters when possible.
if clipboard.userContent.converters.len > 0:
- return constructClipboardContent(clipboard.userContent.toString(mimeType), kind, mimeType)
+ return constructClipboardContent(
+ clipboard.userContent.toString(mimeType), kind, mimeType
+ )
return constructClipboardContent("", kind, mimeType)
- var fds: array[2, FileHandle] # [0] - read, [1] - write
- if pipe(fds) < 0: #? use O_NONBLOCK?
+ var fds: array[2, FileHandle] # [0] - read, [1] - write
+ if pipe(fds) < 0: #? use O_NONBLOCK?
raiseOSError(osLastError())
clipboard.globals.current_selection_data_offer.receive(mimeType.cstring, fds[1])
@@ -1708,20 +2040,19 @@ method content*(
let c = read(fds[0], cbuffer[0].addr, cbuffer.len)
if c <= 0:
break
- for i in 0.. 0:
for mimeType in offeredMimeTypes:
clipboard.dataSource.offer(mimeType.cstring)
-
+
clipboard.dataSource.onSend:
let data = content.toString($mimeType)
discard write(fd, data.cstring, data.len)
discard close fd
-
else:
clipboard.dataSource.proxy.raw = nil
if clipboard == clipboard.globals.primaryClipboard.CLipboardWayland:
- clipboard.globals.dataDevice.set_selection(clipboard.dataSource, clipboard.globals.lastSeatEventSerial)
-
- discard wl_display_roundtrip clipboard.globals.display
+ clipboard.globals.dataDevice.set_selection(
+ clipboard.dataSource, clipboard.globals.lastSeatEventSerial
+ )
+ discard wl_display_roundtrip clipboard.globals.display
method firstStep*(window: WindowWayland, makeVisible = true) =
if makeVisible:
@@ -1776,16 +2105,18 @@ method firstStep*(window: WindowWayland, makeVisible = true) =
discard libdecor_dispatch(window.globals.libdecorCtx, 0)
discard wl_display_roundtrip window.globals.display
- if window.opened: window.eventsHandler.onResize.pushEvent ResizeEvent(window: window, size: window.size, initial: true)
+ if window.opened:
+ window.eventsHandler.onResize.pushEvent ResizeEvent(
+ window: window, size: window.size, initial: true
+ )
window.lastTickTime = getTime()
redraw window
-
method step*(window: WindowWayland) =
## make window main loop step
## ! don't forget to call firstStep()
- template closeIfNeeded =
+ template closeIfNeeded() =
if window.m_closed:
window.eventsHandler.onClose.pushEvent CloseEvent(window: window)
release window
@@ -1798,24 +2129,30 @@ method step*(window: WindowWayland) =
let eventCount = wl_display_roundtrip(window.globals.display)
if eventCount < 0:
- raise newException(RoundtripFailed, "wl_display_roundtrip() returned " & $eventCount)
+ raise
+ newException(RoundtripFailed, "wl_display_roundtrip() returned " & $eventCount)
closeIfNeeded()
- if eventCount <= 2: # seems like idle event count is 2
+ if eventCount <= 2: # seems like idle event count is 2
sleep(1)
# repeat keys if needed
if (
- window.globals.seat_keyboard_repeatSettings.rate > 0 and
- window.lastPressedRawKeyDown
+ window.globals.seat_keyboard_repeatSettings.rate > 0 and window.lastPressedRawKeyDown
):
- let repeatStartTime = window.lastPressedKeyTime + initDuration(milliseconds = window.globals.seat_keyboard_repeatSettings.delay)
+ let repeatStartTime =
+ window.lastPressedKeyTime +
+ initDuration(milliseconds = window.globals.seat_keyboard_repeatSettings.delay)
let nows = getTime()
- let interval = initDuration(milliseconds = max(1'i64, (1000 div window.globals.seat_keyboard_repeatSettings.rate).int64))
+ let interval = initDuration(
+ milliseconds =
+ max(1'i64, (1000 div window.globals.seat_keyboard_repeatSettings.rate).int64)
+ )
- if repeatStartTime <= nows and window.lastKeyRepeatedTime < repeatStartTime - interval:
+ if repeatStartTime <= nows and
+ window.lastKeyRepeatedTime < repeatStartTime - interval:
window.lastKeyRepeatedTime = repeatStartTime - interval
-
+
while repeatStartTime <= nows and window.lastKeyRepeatedTime + interval <= nows:
window.lastKeyRepeatedTime += interval
@@ -1829,23 +2166,37 @@ method step*(window: WindowWayland) =
if repeatedKey != Key.unknown and window.keyboard.pressed.contains(repeatedKey):
window.keyboard.pressed.excl repeatedKey
window.refreshKeyboardModifiers()
- if window.opened: window.eventsHandler.onKey.pushEvent KeyEvent(
- window: window, key: repeatedKey, pressed: false, repeated: true, modifiers: window.keyboard.modifiers
- )
+ if window.opened:
+ window.eventsHandler.onKey.pushEvent KeyEvent(
+ window: window,
+ key: repeatedKey,
+ pressed: false,
+ repeated: true,
+ modifiers: window.keyboard.modifiers,
+ )
window.keyboard.pressed.incl repeatedKey
window.refreshKeyboardModifiers()
- if window.opened: window.eventsHandler.onKey.pushEvent KeyEvent(
- window: window, key: repeatedKey, pressed: true, repeated: true, modifiers: window.keyboard.modifiers
- )
+ if window.opened:
+ window.eventsHandler.onKey.pushEvent KeyEvent(
+ window: window,
+ key: repeatedKey,
+ pressed: true,
+ repeated: true,
+ modifiers: window.keyboard.modifiers,
+ )
if repeatedText != "":
window.lastTextEntered = repeatedText
- if window.opened: window.eventsHandler.onTextInput.pushEvent TextInputEvent(
- window: window, text: repeatedText, repeated: true
- )
+ if window.opened:
+ window.eventsHandler.onTextInput.pushEvent TextInputEvent(
+ window: window, text: repeatedText, repeated: true
+ )
let nows = getTime()
- if window.opened: window.eventsHandler.onTick.pushEvent TickEvent(window: window, deltaTime: nows - window.lastTickTime)
+ if window.opened:
+ window.eventsHandler.onTick.pushEvent TickEvent(
+ window: window, deltaTime: nows - window.lastTickTime
+ )
closeIfNeeded()
window.lastTickTime = nows
@@ -1853,30 +2204,51 @@ method step*(window: WindowWayland) =
window.redrawRequested = false
if window.m_visible:
- if window.opened: window.eventsHandler.onRender.pushEvent RenderEvent(window: window)
+ if window.opened:
+ window.eventsHandler.onRender.pushEvent RenderEvent(window: window)
closeIfNeeded()
window.swapBuffers()
wl_display_flush window.globals.display
-
proc newSoftwareRenderingWindowWayland*(
- globals: SiwinGlobalsWayland,
- size = ivec2(1280, 720),
- title = "",
- screen: ScreenWayland,
- resizable = true,
- fullscreen = false,
- frameless = false,
- transparent = false,
-
- class = "", # window class (used on linux), equals to title if not specified
+ globals: SiwinGlobalsWayland,
+ size = ivec2(1280, 720),
+ title = "",
+ screen: ScreenWayland,
+ resizable = true,
+ fullscreen = false,
+ frameless = false,
+ transparent = false,
+ class = "", # window class (used on linux), equals to title if not specified
): WindowWaylandSoftwareRendering =
new result
result.globals = globals
- result.initSoftwareRenderingWindow(size, screen, fullscreen, frameless, transparent, (if class == "": title else: class))
+ result.initSoftwareRenderingWindow(
+ size,
+ screen,
+ fullscreen,
+ frameless,
+ transparent,
+ (if class == "": title else: class),
+ )
result.title = title
- if not resizable: result.resizable = false
+ if not resizable:
+ result.resizable = false
+
+proc newPopupWindowWayland*(
+ globals: SiwinGlobalsWayland,
+ parent: WindowWayland,
+ placement: PopupPlacement,
+ transparent = false,
+ grab = true,
+): WindowWaylandSoftwareRendering =
+ if parent == nil:
+ raise ValueError.newException("Popup windows require a parent window")
+
+ new result
+ result.globals = globals
+ result.initPopupWindow(parent, placement, grab, transparent)
export Layer, LayerEdge, LayerInteractivityMode
diff --git a/src/siwin/platforms/winapi/window.nim b/src/siwin/platforms/winapi/window.nim
index 03d56dc..b79d24c 100644
--- a/src/siwin/platforms/winapi/window.nim
+++ b/src/siwin/platforms/winapi/window.nim
@@ -11,7 +11,7 @@ privateAccess Window
type
ScreenWinapi* = ref object of Screen
-
+
Buffer = object
x, y: int
bitmap: HBitmap
@@ -33,117 +33,123 @@ type
WindowWinapiSoftwareRendering* = ref object of WindowWinapi
buffer: Buffer
+proc popupWindowPos(window: WindowWinapi, placement: PopupPlacement): IVec2 =
+ let parent = window.parentWindow()
+ if parent == nil:
+ placement.popupRelativePos()
+ else:
+ parent.pos + placement.popupRelativePos()
proc wkeyToKey(key: WParam): Key =
case key
- of Vk_lshift: Key.lshift
- of Vk_rshift: Key.rshift
- of Vk_lmenu: Key.lalt
- of Vk_rmenu: Key.ralt
- of Vk_lcontrol: Key.lcontrol
- of Vk_rcontrol: Key.rcontrol
- of Vk_lwin: Key.lsystem
- of Vk_rwin: Key.rsystem
- of Vk_apps: Key.menu
- of Vk_escape: Key.escape
- of Vk_oem1: Key.semicolon
- of Vk_oem2: Key.slash
- of Vk_oem_plus: Key.equal
- of Vk_oem_minus: Key.minus
- of Vk_oem4: Key.lbracket
- of Vk_oem6: Key.rbracket
- of Vk_oem_comma: Key.comma
- of Vk_oem_period: Key.dot
- of Vk_oem7: Key.quote
- of Vk_oem5: Key.backslash
- of Vk_oem3: Key.tilde
- of Vk_space: Key.space
- of Vk_return: Key.enter
- of Vk_back: Key.backspace
- of Vk_tab: Key.tab
- of Vk_prior: Key.page_up
- of Vk_next: Key.page_down
- of Vk_end: Key.End
- of Vk_home: Key.home
- of Vk_insert: Key.insert
- of Vk_delete: Key.del
- of Vk_add: Key.add
- of Vk_subtract: Key.subtract
- of Vk_multiply: Key.multiply
- of Vk_divide: Key.divide
- of Vk_capital: Key.capsLock
- of Vk_numLock: Key.numLock
- of Vk_scroll: Key.scrollLock
- of Vk_snapshot: Key.printScreen
- of Vk_print: Key.printScreen
- of Vk_decimal: Key.npadDot
- of Vk_pause: Key.pause
- of Vk_f1: Key.f1
- of Vk_f2: Key.f2
- of Vk_f3: Key.f3
- of Vk_f4: Key.f4
- of Vk_f5: Key.f5
- of Vk_f6: Key.f6
- of Vk_f7: Key.f7
- of Vk_f8: Key.f8
- of Vk_f9: Key.f9
- of Vk_f10: Key.f10
- of Vk_f11: Key.f11
- of Vk_f12: Key.f12
- of Vk_f13: Key.f13
- of Vk_f14: Key.f14
- of Vk_f15: Key.f15
- of Vk_left: Key.left
- of Vk_right: Key.right
- of Vk_up: Key.up
- of Vk_down: Key.down
- of Vk_numpad0: Key.npad0
- of Vk_numpad1: Key.npad1
- of Vk_numpad2: Key.npad2
- of Vk_numpad3: Key.npad3
- of Vk_numpad4: Key.npad4
- of Vk_numpad5: Key.npad5
- of Vk_numpad6: Key.npad6
- of Vk_numpad7: Key.npad7
- of Vk_numpad8: Key.npad8
- of Vk_numpad9: Key.npad9
- of 'A'.ord: Key.a
- of 'B'.ord: Key.b
- of 'C'.ord: Key.c
- of 'D'.ord: Key.d
- of 'E'.ord: Key.e
- of 'F'.ord: Key.f
- of 'G'.ord: Key.g
- of 'H'.ord: Key.h
- of 'I'.ord: Key.i
- of 'J'.ord: Key.j
- of 'K'.ord: Key.k
- of 'L'.ord: Key.l
- of 'M'.ord: Key.m
- of 'N'.ord: Key.n
- of 'O'.ord: Key.o
- of 'P'.ord: Key.p
- of 'Q'.ord: Key.q
- of 'R'.ord: Key.r
- of 'S'.ord: Key.s
- of 'T'.ord: Key.t
- of 'U'.ord: Key.u
- of 'V'.ord: Key.v
- of 'W'.ord: Key.w
- of 'X'.ord: Key.x
- of 'Y'.ord: Key.y
- of 'Z'.ord: Key.z
- of '0'.ord: Key.n0
- of '1'.ord: Key.n1
- of '2'.ord: Key.n2
- of '3'.ord: Key.n3
- of '4'.ord: Key.n4
- of '5'.ord: Key.n5
- of '6'.ord: Key.n6
- of '7'.ord: Key.n7
- of '8'.ord: Key.n8
- of '9'.ord: Key.n9
- else: Key.unknown
+ of Vk_lshift: Key.lshift
+ of Vk_rshift: Key.rshift
+ of Vk_lmenu: Key.lalt
+ of Vk_rmenu: Key.ralt
+ of Vk_lcontrol: Key.lcontrol
+ of Vk_rcontrol: Key.rcontrol
+ of Vk_lwin: Key.lsystem
+ of Vk_rwin: Key.rsystem
+ of Vk_apps: Key.menu
+ of Vk_escape: Key.escape
+ of Vk_oem1: Key.semicolon
+ of Vk_oem2: Key.slash
+ of Vk_oem_plus: Key.equal
+ of Vk_oem_minus: Key.minus
+ of Vk_oem4: Key.lbracket
+ of Vk_oem6: Key.rbracket
+ of Vk_oem_comma: Key.comma
+ of Vk_oem_period: Key.dot
+ of Vk_oem7: Key.quote
+ of Vk_oem5: Key.backslash
+ of Vk_oem3: Key.tilde
+ of Vk_space: Key.space
+ of Vk_return: Key.enter
+ of Vk_back: Key.backspace
+ of Vk_tab: Key.tab
+ of Vk_prior: Key.page_up
+ of Vk_next: Key.page_down
+ of Vk_end: Key.End
+ of Vk_home: Key.home
+ of Vk_insert: Key.insert
+ of Vk_delete: Key.del
+ of Vk_add: Key.add
+ of Vk_subtract: Key.subtract
+ of Vk_multiply: Key.multiply
+ of Vk_divide: Key.divide
+ of Vk_capital: Key.capsLock
+ of Vk_numLock: Key.numLock
+ of Vk_scroll: Key.scrollLock
+ of Vk_snapshot: Key.printScreen
+ of Vk_print: Key.printScreen
+ of Vk_decimal: Key.npadDot
+ of Vk_pause: Key.pause
+ of Vk_f1: Key.f1
+ of Vk_f2: Key.f2
+ of Vk_f3: Key.f3
+ of Vk_f4: Key.f4
+ of Vk_f5: Key.f5
+ of Vk_f6: Key.f6
+ of Vk_f7: Key.f7
+ of Vk_f8: Key.f8
+ of Vk_f9: Key.f9
+ of Vk_f10: Key.f10
+ of Vk_f11: Key.f11
+ of Vk_f12: Key.f12
+ of Vk_f13: Key.f13
+ of Vk_f14: Key.f14
+ of Vk_f15: Key.f15
+ of Vk_left: Key.left
+ of Vk_right: Key.right
+ of Vk_up: Key.up
+ of Vk_down: Key.down
+ of Vk_numpad0: Key.npad0
+ of Vk_numpad1: Key.npad1
+ of Vk_numpad2: Key.npad2
+ of Vk_numpad3: Key.npad3
+ of Vk_numpad4: Key.npad4
+ of Vk_numpad5: Key.npad5
+ of Vk_numpad6: Key.npad6
+ of Vk_numpad7: Key.npad7
+ of Vk_numpad8: Key.npad8
+ of Vk_numpad9: Key.npad9
+ of 'A'.ord: Key.a
+ of 'B'.ord: Key.b
+ of 'C'.ord: Key.c
+ of 'D'.ord: Key.d
+ of 'E'.ord: Key.e
+ of 'F'.ord: Key.f
+ of 'G'.ord: Key.g
+ of 'H'.ord: Key.h
+ of 'I'.ord: Key.i
+ of 'J'.ord: Key.j
+ of 'K'.ord: Key.k
+ of 'L'.ord: Key.l
+ of 'M'.ord: Key.m
+ of 'N'.ord: Key.n
+ of 'O'.ord: Key.o
+ of 'P'.ord: Key.p
+ of 'Q'.ord: Key.q
+ of 'R'.ord: Key.r
+ of 'S'.ord: Key.s
+ of 'T'.ord: Key.t
+ of 'U'.ord: Key.u
+ of 'V'.ord: Key.v
+ of 'W'.ord: Key.w
+ of 'X'.ord: Key.x
+ of 'Y'.ord: Key.y
+ of 'Z'.ord: Key.z
+ of '0'.ord: Key.n0
+ of '1'.ord: Key.n1
+ of '2'.ord: Key.n2
+ of '3'.ord: Key.n3
+ of '4'.ord: Key.n4
+ of '5'.ord: Key.n5
+ of '6'.ord: Key.n6
+ of '7'.ord: Key.n7
+ of '8'.ord: Key.n8
+ of '9'.ord: Key.n9
+ else: Key.unknown
proc wkeyToKey(key: WParam, flags: LParam): Key =
let scancode = ((flags and 0xff0000) shr 16).Uint
@@ -155,26 +161,33 @@ proc wkeyToKey(key: WParam, flags: LParam): Key =
if (flags and 0x1000000) != 0: Key.ralt else: Key.lalt
of Vk_control:
if (flags and 0x1000000) != 0: Key.rcontrol else: Key.lcontrol
- else: wkeyToKey(key)
-
+ else:
+ wkeyToKey(key)
# todo: multiscreen support
-proc screenCountWinapi*(): int32 = 1
+proc screenCountWinapi*(): int32 =
+ 1
+
+proc screenWinapi*(number: int32): ScreenWinapi =
+ new result
+
+proc defaultScreenWinapi*(): ScreenWinapi =
+ screenWinapi(0)
-proc screenWinapi*(number: int32): ScreenWinapi = new result
-proc defaultScreenWinapi*(): ScreenWinapi = screenWinapi(0)
-method number*(screen: ScreenWinapi): int32 = 0
+method number*(screen: ScreenWinapi): int32 =
+ 0
-method width*(screen: ScreenWinapi): int32 = GetSystemMetrics(SmCxScreen)
-method height*(screen: ScreenWinapi): int32 = GetSystemMetrics(SmCyScreen)
+method width*(screen: ScreenWinapi): int32 =
+ GetSystemMetrics(SmCxScreen)
+method height*(screen: ScreenWinapi): int32 =
+ GetSystemMetrics(SmCyScreen)
proc `=destroy`(buffer: Buffer) {.siwin_destructor.} =
if buffer.hdc != 0:
DeleteDC buffer.hdc
DeleteObject buffer.bitmap
-
proc `=destroy`(window: WindowWinapiObj) {.siwin_destructor.} =
if window.hdc != 0:
DeleteDC window.hdc
@@ -185,14 +198,23 @@ proc `=destroy`(window: WindowWinapiObj) {.siwin_destructor.} =
if window.wcursor != 0:
DestroyCursor window.wcursor
+proc poolEvent(
+ window: WindowWinapi, message: Uint, wParam: WParam, lParam: LParam
+): LResult
-proc poolEvent(window: WindowWinapi, message: Uint, wParam: WParam, lParam: LParam): LResult
-
-proc windowProc(handle: HWnd, message: Uint, wParam: WParam, lParam: LParam): LResult {.stdcall.} =
- let win = if handle != 0: cast[WindowWinapi](GetWindowLongPtr(handle, GwlpUserData)) else: nil
+proc windowProc(
+ handle: HWnd, message: Uint, wParam: WParam, lParam: LParam
+): LResult {.stdcall.} =
+ let win =
+ if handle != 0:
+ cast[WindowWinapi](GetWindowLongPtr(handle, GwlpUserData))
+ else:
+ nil
- if win != nil: win.poolEvent(message, wParam, lParam)
- else: DefWindowProc(handle, message, wParam, lParam)
+ if win != nil:
+ win.poolEvent(message, wParam, lParam)
+ else:
+ DefWindowProc(handle, message, wParam, lParam)
const
wClassName = L"w"
@@ -200,11 +222,11 @@ const
block winapiInit:
var wcex = WndClassEx(
- cbSize: WndClassEx.sizeof.int32,
- style: CsHRedraw or CsVRedraw or CsDblClks,
- hInstance: hInstance,
- hCursor: LoadCursor(0, IdcArrow),
- lpfnWndProc: windowProc,
+ cbSize: WndClassEx.sizeof.int32,
+ style: CsHRedraw or CsVRedraw or CsDblClks,
+ hInstance: hInstance,
+ hCursor: LoadCursor(0, IdcArrow),
+ lpfnWndProc: windowProc,
lpszClassName: wClassName,
)
RegisterClassEx(wcex.addr)
@@ -217,7 +239,8 @@ template pushEvent(eventsHandler: WindowEventsHandler, event, args) =
eventsHandler.event(args)
method `fullscreen=`*(window: WindowWinapi, v: bool) =
- if window.m_fullscreen == v: return
+ if window.m_fullscreen == v:
+ return
window.m_fullscreen = v
if v:
window.handle.SetWindowLongPtr(GwlStyle, WsVisible)
@@ -225,18 +248,25 @@ method `fullscreen=`*(window: WindowWinapi, v: bool) =
else:
window.handle.ShowWindow(SwShowNormal)
discard window.handle.SetWindowLongPtr(GwlStyle, WsVisible or WsOverlappedWindow)
- window.eventsHandler.pushEvent onStateBoolChanged, StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.fullscreen, value: v
- )
+ window.eventsHandler.pushEvent onStateBoolChanged,
+ StateBoolChangedEvent(
+ window: window, kind: StateBoolChangedEventKind.fullscreen, value: v
+ )
method `size=`*(window: WindowWinapi, size: IVec2) =
+ if window.isPopup:
+ var placement = window.placement
+ placement.size = size
+ window.placement = placement
+ return
window.fullscreen = false
let rcClient = window.handle.clientRect
var rcWind = window.handle.windowRect
let borderx = (rcWind.right - rcWind.left) - rcClient.right
let bordery = (rcWind.bottom - rcWind.top) - rcClient.bottom
- window.handle.MoveWindow(rcWind.left, rcWind.top, (size.x + borderx).int32, (size.y + bordery).int32, True)
-
+ window.handle.MoveWindow(
+ rcWind.left, rcWind.top, (size.x + borderx).int32, (size.y + bordery).int32, True
+ )
proc enableTransparency*(window: WindowWinapi) =
let region = CreateRectRgn(0, 0, -1, -1)
@@ -245,35 +275,49 @@ proc enableTransparency*(window: WindowWinapi) =
var bb = DwmBlurBehind()
bb.dwFlags = DwmbbEnable or DwmbbBlurRegion
bb.fEnable = True
- bb.hRgnBlur = region # "blur" somewhere outside the window, ideally nothing
+ bb.hRgnBlur = region # "blur" somewhere outside the window, ideally nothing
window.handle.DwmEnableBlurBehindWindow(bb.addr)
DeleteObject(region)
-
-proc initWindow(window: WindowWinapi; size: IVec2; screen: ScreenWinapi, fullscreen, frameless, transparent: bool, class = wClassName) =
+proc initWindow(
+ window: WindowWinapi,
+ size: IVec2,
+ screen: ScreenWinapi,
+ fullscreen, frameless, transparent: bool,
+ class = wClassName,
+ owner: HWnd = 0,
+ popup = false,
+) =
window.handle = CreateWindow(
class,
"",
- if frameless: WsPopup or WsMinimizeBox
- else: WsOverlappedWindow,
- CwUseDefault, CwUseDefault,
- size.x, size.y,
- 0, 0,
+ if popup:
+ WsPopup
+ elif frameless:
+ WsPopup or WsMinimizeBox
+ else:
+ WsOverlappedWindow,
+ CwUseDefault,
+ CwUseDefault,
+ size.x,
+ size.y,
+ owner,
+ 0,
hInstance,
- nil
+ nil,
)
discard ShowWindow(window.handle, SwHide)
window.m_frameless = frameless
- window.m_focused = true #? is it correct?
+ window.m_focused = true #? is it correct?
window.wcursor = LoadCursor(0, IdcArrow)
window.handle.SetWindowLongPtrW(GwlpUserData, cast[LongPtr](window))
window.handle.trackMouseEvent(TmeHover)
window.handle.trackMouseEvent(TmeLeave)
window.hdc = window.handle.GetDC
-
+
window.m_size = size
if fullscreen:
window.m_fullscreen = true
@@ -283,77 +327,140 @@ proc initWindow(window: WindowWinapi; size: IVec2; screen: ScreenWinapi, fullscr
if transparent:
window.m_transparent = true
window.enableTransparency()
-
- window.m_clipboard = ClipboardWinapi(availableKinds: {ClipboardContentKind.text}) # todo: other types
- window.m_selectionClipboard = window.m_clipboard
- window.m_dragndropClipboard = ClipboardWinapiDnd() # todo
+ window.m_clipboard = ClipboardWinapi(availableKinds: {ClipboardContentKind.text})
+ # todo: other types
+ window.m_selectionClipboard = window.m_clipboard
+ window.m_dragndropClipboard = ClipboardWinapiDnd() # todo
+
+proc initPopupWindow(
+ window: WindowWinapiSoftwareRendering,
+ parent: WindowWinapi,
+ placement: PopupPlacement,
+ transparent: bool,
+ grab: bool,
+) =
+ window.initPopupState(parent, placement, grab)
+ window.initWindow(
+ placement.popupSize(),
+ ScreenWinapi(),
+ false,
+ true,
+ transparent,
+ owner = parent.handle,
+ popup = true,
+ )
+ window.m_resizable = false
+ window.pos = window.popupWindowPos(placement)
method `title=`*(window: WindowWinapi, title: string) =
window.handle.SetWindowText(title)
method close*(window: WindowWinapi) =
- if not window.m_closed: window.handle.SendMessage(WmClose, 0, 0)
-
+ if window.m_closed:
+ return
+ window.notifyPopupDone(PopupDismissReason.pdrClientClosed)
+ window.handle.SendMessage(WmClose, 0, 0)
method `pos=`*(window: WindowWinapi, v: IVec2) =
- if window.m_fullscreen: return
+ if window.isPopup:
+ return
+ if window.m_fullscreen:
+ return
window.handle.SetWindowPos(0, v.x, v.y, 0, 0, SwpNoSize)
+method `placement=`*(window: WindowWinapi, v: PopupPlacement) =
+ window.m_popupPlacement = v
+ let size = v.popupSize()
+ window.m_size = size
+ let pos = window.popupWindowPos(v)
+ window.m_pos = pos
+ window.handle.SetWindowPos(0, pos.x, pos.y, size.x, size.y, 0)
+
+method reposition*(window: WindowWinapi, v: PopupPlacement) =
+ window.placement = v
method `cursor=`*(window: WindowWinapi, v: Cursor) =
- if window.m_cursor.kind == builtin and v.kind == builtin and v.builtin == window.m_cursor.builtin: return
- if window.wcursor != 0: DestroyCursor window.wcursor
+ if window.m_cursor.kind == builtin and v.kind == builtin and
+ v.builtin == window.m_cursor.builtin:
+ return
+ if window.wcursor != 0:
+ DestroyCursor window.wcursor
window.m_cursor = v
case v.kind
of builtin:
- var cu: HCursor = case v.builtin
- of BuiltinCursor.arrow: LoadCursor(0, IdcArrow)
- of BuiltinCursor.arrowUp: LoadCursor(0, IdcUpArrow)
- of BuiltinCursor.pointingHand: LoadCursor(0, IdcHand)
- of BuiltinCursor.arrowRight: LoadCursor(0, IdcArrow) #! no needed cursor
- of BuiltinCursor.wait: LoadCursor(0, IdcWait)
- of BuiltinCursor.arrowWait: LoadCursor(0, IdcAppStarting)
- of BuiltinCursor.grab: LoadCursor(0, IdcHand) #! no needed cursor
- of BuiltinCursor.text: LoadCursor(0, IdcIBeam)
- of BuiltinCursor.cross: LoadCursor(0, IdcCross)
- of BuiltinCursor.sizeAll: LoadCursor(0, IdcSizeAll)
- of BuiltinCursor.sizeVertical: LoadCursor(0, IdcSizens)
- of BuiltinCursor.sizeHorizontal: LoadCursor(0, IdcSizewe)
- of BuiltinCursor.sizeTopLeft: LoadCursor(0, IdcSizenwse)
- of BuiltinCursor.sizeTopRight: LoadCursor(0, IdcSizenesw)
- of BuiltinCursor.sizeBottomLeft: LoadCursor(0, IdcSizenesw)
- of BuiltinCursor.sizeBottomRight: LoadCursor(0, IdcSizenwse)
- of BuiltinCursor.hided: LoadCursor(0, IdcNo)
+ var cu: HCursor =
+ case v.builtin
+ of BuiltinCursor.arrow:
+ LoadCursor(0, IdcArrow)
+ of BuiltinCursor.arrowUp:
+ LoadCursor(0, IdcUpArrow)
+ of BuiltinCursor.pointingHand:
+ LoadCursor(0, IdcHand)
+ of BuiltinCursor.arrowRight:
+ LoadCursor(0, IdcArrow)
+ #! no needed cursor
+ of BuiltinCursor.wait:
+ LoadCursor(0, IdcWait)
+ of BuiltinCursor.arrowWait:
+ LoadCursor(0, IdcAppStarting)
+ of BuiltinCursor.grab:
+ LoadCursor(0, IdcHand)
+ #! no needed cursor
+ of BuiltinCursor.text:
+ LoadCursor(0, IdcIBeam)
+ of BuiltinCursor.cross:
+ LoadCursor(0, IdcCross)
+ of BuiltinCursor.sizeAll:
+ LoadCursor(0, IdcSizeAll)
+ of BuiltinCursor.sizeVertical:
+ LoadCursor(0, IdcSizens)
+ of BuiltinCursor.sizeHorizontal:
+ LoadCursor(0, IdcSizewe)
+ of BuiltinCursor.sizeTopLeft:
+ LoadCursor(0, IdcSizenwse)
+ of BuiltinCursor.sizeTopRight:
+ LoadCursor(0, IdcSizenesw)
+ of BuiltinCursor.sizeBottomLeft:
+ LoadCursor(0, IdcSizenesw)
+ of BuiltinCursor.sizeBottomRight:
+ LoadCursor(0, IdcSizenwse)
+ of BuiltinCursor.hided:
+ LoadCursor(0, IdcNo)
if cu != 0:
SetCursor cu
window.wcursor = cu
-
of image:
if v.image.pixels.size.x * v.image.pixels.size.y == 0:
window.cursor = Cursor(kind: builtin, builtin: BuiltinCursor.hided)
return
- if window.wcursor != 0: DestroyCursor window.wcursor
+ if window.wcursor != 0:
+ DestroyCursor window.wcursor
- let sourceFormat = v.image.pixels.format # to convert pixels back later
+ let sourceFormat = v.image.pixels.format # to convert pixels back later
var buffer = v.image.pixels
- convertPixelsInplace(buffer.data, buffer.size, sourceFormat, PixelBufferFormat.bgra_32bit)
+ convertPixelsInplace(
+ buffer.data, buffer.size, sourceFormat, PixelBufferFormat.bgra_32bit
+ )
- window.wcursor = CreateIcon(hInstance, buffer.size.x, buffer.size.y, 1, 32, nil, cast[ptr Byte](buffer.data))
+ window.wcursor = CreateIcon(
+ hInstance, buffer.size.x, buffer.size.y, 1, 32, nil, cast[ptr Byte](buffer.data)
+ )
SetCursor window.wcursor
- convertPixelsInplace(buffer.data, buffer.size, PixelBufferFormat.bgra_32bit, sourceFormat)
-
+ convertPixelsInplace(
+ buffer.data, buffer.size, PixelBufferFormat.bgra_32bit, sourceFormat
+ )
method `icon=`*(window: WindowWinapi, _: nil.typeof) =
## clear icon
if window.wicon != 0:
DestroyIcon window.wicon
window.wicon = 0
-
+
window.handle.SendMessageW(WmSetIcon, IconBig, 0)
window.handle.SendMessageW(WmSetIcon, IconSmall, 0)
@@ -363,66 +470,83 @@ method `icon=`*(window: WindowWinapi, v: PixelBuffer) =
window.icon = nil
return
- if window.wicon != 0: DestroyIcon window.wicon
-
- let sourceFormat = v.format # to convert pixels back later
+ if window.wicon != 0:
+ DestroyIcon window.wicon
+
+ let sourceFormat = v.format # to convert pixels back later
var buffer = v
- convertPixelsInplace(buffer.data, buffer.size, sourceFormat, PixelBufferFormat.bgra_32bit)
+ convertPixelsInplace(
+ buffer.data, buffer.size, sourceFormat, PixelBufferFormat.bgra_32bit
+ )
- window.wicon = CreateIcon(hInstance, v.size.x, v.size.y, 1, 32, nil, cast[ptr Byte](v.data))
+ window.wicon =
+ CreateIcon(hInstance, v.size.x, v.size.y, 1, 32, nil, cast[ptr Byte](v.data))
window.handle.SendMessageW(WmSetIcon, IconBig, window.wicon)
window.handle.SendMessageW(WmSetIcon, IconSmall, window.wicon)
- convertPixelsInplace(buffer.data, buffer.size, PixelBufferFormat.bgra_32bit, sourceFormat)
-
+ convertPixelsInplace(
+ buffer.data, buffer.size, PixelBufferFormat.bgra_32bit, sourceFormat
+ )
proc resizeBufferIfNeeded(buffer: var Buffer, size: IVec2) =
if size.x != buffer.x or size.y != buffer.y:
if buffer.hdc != 0:
DeleteDC buffer.hdc
DeleteObject buffer.bitmap
-
+
buffer.x = size.x
buffer.y = size.y
-
+
var bmi = BitmapInfo(
bmiHeader: BitmapInfoHeader(
- biSize: BitmapInfoHeader.sizeof.int32, biWidth: size.x.Long, biHeight: -size.y.Long,
- biPlanes: 1, biBitCount: 32, biCompression: Bi_rgb
+ biSize: BitmapInfoHeader.sizeof.int32,
+ biWidth: size.x.Long,
+ biHeight: -size.y.Long,
+ biPlanes: 1,
+ biBitCount: 32,
+ biCompression: Bi_rgb,
)
)
- buffer.bitmap = CreateDibSection(0, bmi.addr, Dib_rgb_colors, cast[ptr pointer](buffer.pixels.addr), 0, 0)
+ buffer.bitmap = CreateDibSection(
+ 0, bmi.addr, Dib_rgb_colors, cast[ptr pointer](buffer.pixels.addr), 0, 0
+ )
buffer.hdc = CreateCompatibleDC(0)
buffer.hdc.SelectObject buffer.bitmap
-
method pixelBuffer*(window: WindowWinapiSoftwareRendering): PixelBuffer =
result = PixelBuffer(
data: window.buffer.pixels,
size: ivec2(window.buffer.x.int32, window.buffer.y.int32),
- format: (if window.transparent: PixelBufferFormat.bgrx_32bit else: PixelBufferFormat.bgru_32bit)
+ format: (
+ if window.transparent: PixelBufferFormat.bgrx_32bit
+ else: PixelBufferFormat.bgru_32bit
+ ),
)
-
proc releaseAllKeys(window: WindowWinapi) =
for key in window.keyboard.pressed:
window.keyboard.pressed.excl key
- window.eventsHandler.pushEvent onKey, KeyEvent(window: window, key: key, pressed: false, repeated: false, generated: true)
+ window.eventsHandler.pushEvent onKey,
+ KeyEvent(
+ window: window, key: key, pressed: false, repeated: false, generated: true
+ )
for button in window.mouse.pressed:
window.mouse.pressed.excl button
- window.eventsHandler.pushEvent onMouseButton, MouseButtonEvent(window: window, button: button, pressed: false, generated: true)
-
+ window.eventsHandler.pushEvent onMouseButton,
+ MouseButtonEvent(window: window, button: button, pressed: false, generated: true)
method `maximized=`*(window: WindowWinapi, v: bool) =
- if window.m_maximized == v: return
+ if window.m_maximized == v:
+ return
if window.m_frameless:
if v:
window.restoreSize = window.size
window.restorePos = window.pos
var workArea: Rect
discard SystemParametersInfo(SpiGetWorkArea, 0, workArea.addr, 0)
- window.size = ivec2(workArea.right - workArea.left, workArea.bottom - workArea.top)
+ window.size =
+ ivec2(workArea.right - workArea.left, workArea.bottom - workArea.top)
window.pos = ivec2(workArea.left, workArea.top)
else:
window.size = window.restoreSize
@@ -430,30 +554,36 @@ method `maximized=`*(window: WindowWinapi, v: bool) =
else:
discard ShowWindow(window.handle, if v: SwMaximize else: SwNormal)
window.m_maximized = v
- window.eventsHandler.pushEvent onStateBoolChanged, StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.maximized, value: window.m_maximized
- )
-
+ window.eventsHandler.pushEvent onStateBoolChanged,
+ StateBoolChangedEvent(
+ window: window,
+ kind: StateBoolChangedEventKind.maximized,
+ value: window.m_maximized,
+ )
method `minimized=`*(window: WindowWinapi, v: bool) =
window.releaseAllKeys()
window.m_minimized = v
- discard ShowWindow(window.handle, if v: SwShowMinNoActive else: SwNormal)
-
+ discard ShowWindow(window.handle, if v: SwShowMinNoActive else: SwNormal)
method `visible=`*(window: WindowWinapi, v: bool) =
window.m_visible = v
discard ShowWindow(window.handle, if v: SwShow else: SwHide)
-
method `resizable=`*(window: WindowWinapi, v: bool) =
if not window.m_frameless:
let style = GetWindowLongW(window.handle, GwlStyle)
- discard SetWindowLongW(window.handle, GwlStyle, if v: style or WsThickframe else: style and not WsThickframe)
+ discard SetWindowLongW(
+ window.handle,
+ GwlStyle,
+ if v:
+ style or WsThickframe
+ else:
+ style and not WsThickframe,
+ )
window.m_minSize = ivec2()
window.m_maxSize = ivec2()
-
method `minSize=`*(window: WindowWinapi, v: IVec2) =
window.m_minSize = v
if not window.m_frameless:
@@ -466,7 +596,6 @@ method `maxSize=`*(window: WindowWinapi, v: IVec2) =
let style = GetWindowLongW(window.handle, GwlStyle)
discard SetWindowLongW(window.handle, GwlStyle, style or WsThickframe)
-
method startInteractiveMove*(window: WindowWinapi, pos: Option[Vec2]) =
window.releaseAllKeys()
ReleaseCapture()
@@ -488,29 +617,31 @@ method startInteractiveResize*(window: WindowWinapi, edge: Edge, pos: Option[Vec
of Edge.topRight: 0xf005
of Edge.bottom: 0xf006
of Edge.bottomLeft: 0xf007
- of Edge.bottomRight: 0xf008,
- 0
+ of Edge.bottomRight: 0xf008
+ ,
+ 0,
)
# todo: press all keys and mouse buttons that are pressed after resize
-
method showWindowMenu*(window: WindowWinapi, pos: Option[Vec2]) =
discard
-
-method content*(clipboard: ClipboardWinapi, kind: ClipboardContentKind, mimeType: string): ClipboardContent =
+method content*(
+ clipboard: ClipboardWinapi, kind: ClipboardContentKind, mimeType: string
+): ClipboardContent =
discard OpenClipboard(0)
let hcpb = GetClipboardData(CfUnicodeText)
if hcpb == 0:
CloseClipboard()
return
-
- result = ClipboardContent(kind: ClipboardContentKind.text, text: $cast[PWChar](GlobalLock hcpb)) # todo: other types
+
+ result = ClipboardContent(
+ kind: ClipboardContentKind.text, text: $cast[PWChar](GlobalLock hcpb)
+ ) # todo: other types
GlobalUnlock hcpb
discard CloseClipboard()
-
method `content=`*(clipboard: ClipboardWinapi, content: ClipboardConvertableContent) =
var conv: ClipboardContentConverter
for cv in content.converters:
@@ -520,16 +651,18 @@ method `content=`*(clipboard: ClipboardWinapi, content: ClipboardConvertableCont
else:
## todo
- if conv.f == nil: return
+ if conv.f == nil:
+ return
let content = conv.f(content.data, conv.kind, conv.mimeType)
- if content.kind != ClipboardContentKind.text: return
+ if content.kind != ClipboardContentKind.text:
+ return
let s = content.text
discard OpenClipboard(0)
discard EmptyClipboard()
-
+
let ws = +$s
let ts = (ws.len + 1) * WChar.sizeof
let hstr = GlobalAlloc(GMemMoveable, ts)
@@ -542,54 +675,54 @@ method `content=`*(clipboard: ClipboardWinapi, content: ClipboardConvertableCont
SetClipboardData(CfUnicodeText, hstr)
CloseClipboard()
-
method displayImpl(window: WindowWinapi) {.base.} =
var ps: PaintStruct
window.handle.BeginPaint(ps.addr)
-
+
window.eventsHandler.pushEvent onRender, RenderEvent(window: window)
-
+
if window of WindowWinapiSoftwareRendering:
BitBlt(
window.hdc, 0, 0, window.m_size.x, window.m_size.y,
- window.WindowWinapiSoftwareRendering.buffer.hdc, 0, 0, SrcCopy
+ window.WindowWinapiSoftwareRendering.buffer.hdc, 0, 0, SrcCopy,
)
-
- window.handle.EndPaint(ps.addr)
+ window.handle.EndPaint(ps.addr)
method firstStep*(window: WindowWinapi, makeVisible = true) =
if makeVisible:
window.visible = true
-
+
if window of WindowWinapiSoftwareRendering:
resizeBufferIfNeeded window.WindowWinapiSoftwareRendering.buffer, window.m_size
- window.eventsHandler.pushEvent onResize, ResizeEvent(window: window, size: window.m_size, initial: true)
+ window.eventsHandler.pushEvent onResize,
+ ResizeEvent(window: window, size: window.m_size, initial: true)
window.redrawRequested = true
window.handle.UpdateWindow()
window.lastTickTime = getTime()
-
proc updateWindowState(window: WindowWinapi) =
if not window.m_frameless and IsZoomed(window.handle).bool != window.m_maximized:
window.m_maximized = not window.m_maximized
- window.eventsHandler.pushEvent onStateBoolChanged, StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.maximized, value: window.m_maximized
- )
-
+ window.eventsHandler.pushEvent onStateBoolChanged,
+ StateBoolChangedEvent(
+ window: window,
+ kind: StateBoolChangedEventKind.maximized,
+ value: window.m_maximized,
+ )
+
window.m_minimized = IsIconic(window.handle) != 0
window.m_visible = IsWindowVisible(window.handle) != 0
window.m_resizable = (GetWindowLongW(window.handle, GwlStyle) and WsThickframe) != 0
-
+
var p: WindowPlacement
p.length = sizeof(WindowPlacement).int32
GetWindowPlacement(window.handle, p.addr)
window.m_pos = ivec2(p.rcNormalPosition.left, p.rcNormalPosition.top)
-
method step*(window: WindowWinapi) =
var msg: Msg
var catched = false
@@ -598,7 +731,8 @@ method step*(window: WindowWinapi) =
# make tick if windows sent us WmPaint, it does it when the event queue is empty
if msg.message == WmPaint:
let nows = getTime()
- window.eventsHandler.pushEvent onTick, TickEvent(window: window, deltaTime: nows - window.lastTickTime)
+ window.eventsHandler.pushEvent onTick,
+ TickEvent(window: window, deltaTime: nows - window.lastTickTime)
window.lastTickTime = nows
TranslateMessage(msg.addr)
@@ -609,26 +743,33 @@ method step*(window: WindowWinapi) =
else:
catched = true
- if window.m_closed: return
-
- if not catched: sleep(1)
+ if window.m_closed:
+ return
+ if not catched:
+ sleep(1)
-proc poolEvent(window: WindowWinapi, message: Uint, wParam: WParam, lParam: LParam): LResult =
+proc poolEvent(
+ window: WindowWinapi, message: Uint, wParam: WParam, lParam: LParam
+): LResult =
updateWindowState(window)
- template button: MouseButton =
+ template button(): MouseButton =
case message
- of WM_lbuttonDown, WM_lbuttonUp, WM_lbuttonDblclk: MouseButton.left
- of WM_rbuttonDown, WM_rbuttonUp, WM_rbuttonDblclk: MouseButton.right
- of WM_mbuttonDown, WM_mbuttonUp, WM_mbuttonDblclk: MouseButton.middle
+ of WM_lbuttonDown, WM_lbuttonUp, WM_lbuttonDblclk:
+ MouseButton.left
+ of WM_rbuttonDown, WM_rbuttonUp, WM_rbuttonDblclk:
+ MouseButton.right
+ of WM_mbuttonDown, WM_mbuttonUp, WM_mbuttonDblclk:
+ MouseButton.middle
of WM_xbuttonDown, WM_xbuttonUp, WM_xbuttonDblclk:
let button = wParam.GetXButtonWParam()
case button
of MkXButton1: MouseButton.backward
of MkXButton2: MouseButton.forward
else: MouseButton.left
- else: MouseButton.left
+ else:
+ MouseButton.left
result = 0
@@ -637,114 +778,116 @@ proc poolEvent(window: WindowWinapi, message: Uint, wParam: WParam, lParam: LPar
let rect = window.handle.clientRect
if rect.right != window.m_size.x or rect.bottom != window.m_size.y:
window.m_size = ivec2(rect.right, rect.bottom)
-
+
if window of WindowWinapiSoftwareRendering:
resizeBufferIfNeeded window.WindowWinapiSoftwareRendering.buffer, window.m_size
- window.eventsHandler.pushEvent onResize, ResizeEvent(window: window, size: window.m_size, initial: false)
+ window.eventsHandler.pushEvent onResize,
+ ResizeEvent(window: window, size: window.m_size, initial: false)
window.redrawRequested = true
if window.redrawRequested:
window.redrawRequested = false
if window.m_size.x * window.m_size.y > 0:
window.displayImpl()
-
-
of WmDestroy:
window.m_closed = true
window.eventsHandler.pushEvent onClose, CloseEvent(window: window)
PostQuitMessage(0)
-
of WmMouseMove:
window.mouse.pos = vec2(lParam.GetX_LParam.float32, lParam.GetY_LParam.float32)
window.clicking = {}
- window.eventsHandler.pushEvent onMouseMove, MouseMoveEvent(window: window, pos: window.mouse.pos, kind: MouseMoveKind.move)
-
+ window.eventsHandler.pushEvent onMouseMove,
+ MouseMoveEvent(window: window, pos: window.mouse.pos, kind: MouseMoveKind.move)
of WmMouseLeave:
window.mouse.pos = vec2(lParam.GetX_LParam.float32, lParam.GetY_LParam.float32)
window.clicking = {}
- window.eventsHandler.pushEvent onMouseMove, MouseMoveEvent(window: window, pos: window.mouse.pos, kind: MouseMoveKind.leave)
+ window.eventsHandler.pushEvent onMouseMove,
+ MouseMoveEvent(window: window, pos: window.mouse.pos, kind: MouseMoveKind.leave)
window.handle.trackMouseEvent(TmeHover)
-
of WmMouseHover:
window.mouse.pos = vec2(lParam.GetX_LParam.float32, lParam.GetY_LParam.float32)
window.clicking = {}
- window.eventsHandler.pushEvent onMouseMove, MouseMoveEvent(window: window, pos: window.mouse.pos, kind: MouseMoveKind.enter)
+ window.eventsHandler.pushEvent onMouseMove,
+ MouseMoveEvent(window: window, pos: window.mouse.pos, kind: MouseMoveKind.enter)
window.handle.trackMouseEvent(TmeLeave)
-
of WmMouseWheel:
let delta = if wParam.GetWheelDeltaWParam > 0: -1.0 else: 1.0
window.eventsHandler.pushEvent onScroll, ScrollEvent(window: window, delta: delta)
-
of WmSetFocus:
window.m_focused = true
- window.eventsHandler.pushEvent onStateBoolChanged, StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.focus, value: true
- )
+ window.eventsHandler.pushEvent onStateBoolChanged,
+ StateBoolChangedEvent(
+ window: window, kind: StateBoolChangedEventKind.focus, value: true
+ )
let keys = getKeyboardState().mapit(wkeyToKey(it))
for k in keys: # press pressed in system keys
- if k == Key.unknown: continue
+ if k == Key.unknown:
+ continue
window.keyboard.pressed.incl k
- window.eventsHandler.pushEvent onKey, KeyEvent(window: window, key: k, pressed: false, repeated: false)
-
+ window.eventsHandler.pushEvent onKey,
+ KeyEvent(window: window, key: k, pressed: false, repeated: false)
of WmKillFocus:
window.m_focused = false
- window.eventsHandler.pushEvent onStateBoolChanged, StateBoolChangedEvent(
- window: window, kind: StateBoolChangedEventKind.focus, value: false
- )
+ window.eventsHandler.pushEvent onStateBoolChanged,
+ StateBoolChangedEvent(
+ window: window, kind: StateBoolChangedEventKind.focus, value: false
+ )
let pressed = window.keyboard.pressed
for key in pressed: # release all keys
window.keyboard.pressed.excl key
- window.eventsHandler.pushEvent onKey, KeyEvent(window: window, key: key, pressed: false, repeated: false)
-
+ window.eventsHandler.pushEvent onKey,
+ KeyEvent(window: window, key: key, pressed: false, repeated: false)
of WmLButtonDown, WmRButtonDown, WmMButtonDown, WmXButtonDown:
window.handle.SetCapture()
window.mouse.pressed.incl button
window.clicking.incl button
- window.eventsHandler.pushEvent onMouseButton, MouseButtonEvent(window: window, button: button, pressed: true)
-
+ window.eventsHandler.pushEvent onMouseButton,
+ MouseButtonEvent(window: window, button: button, pressed: true)
of WmLButtonDblclk, WmRButtonDblclk, WmMButtonDblclk, WmXButtonDblclk:
window.handle.SetCapture()
window.mouse.pressed.incl button
- window.eventsHandler.pushEvent onMouseButton, MouseButtonEvent(window: window, button: button, pressed: true)
- window.eventsHandler.pushEvent onClick, ClickEvent(window: window, button: button, pos: window.mouse.pos, double: true)
-
+ window.eventsHandler.pushEvent onMouseButton,
+ MouseButtonEvent(window: window, button: button, pressed: true)
+ window.eventsHandler.pushEvent onClick,
+ ClickEvent(window: window, button: button, pos: window.mouse.pos, double: true)
of WmLButtonUp, WmRButtonUp, WmMButtonUp, WmXButtonUp:
ReleaseCapture()
window.mouse.pressed.excl button
if button in window.clicking:
- window.eventsHandler.pushEvent onClick, ClickEvent(window: window, button: button, pos: window.mouse.pos, double: false)
+ window.eventsHandler.pushEvent onClick,
+ ClickEvent(window: window, button: button, pos: window.mouse.pos, double: false)
window.clicking.excl button
- window.eventsHandler.pushEvent onMouseButton, MouseButtonEvent(window: window, button: button, pressed: false)
-
+ window.eventsHandler.pushEvent onMouseButton,
+ MouseButtonEvent(window: window, button: button, pressed: false)
of WmKeyDown, WmSysKeyDown:
let key = wkeyToKey(wParam, lParam)
if key != Key.unknown:
let repeated = key in window.keyboard.pressed
window.keyboard.pressed.incl key
- window.eventsHandler.pushEvent onKey, KeyEvent(window: window, key: key, pressed: true, repeated: repeated)
-
+ window.eventsHandler.pushEvent onKey,
+ KeyEvent(window: window, key: key, pressed: true, repeated: repeated)
of WmKeyUp, WmSysKeyUp:
let key = wkeyToKey(wParam, lParam)
if key != Key.unknown:
let repeated = key notin window.keyboard.pressed
window.keyboard.pressed.excl key
- window.eventsHandler.pushEvent onKey, KeyEvent(window: window, key: key, pressed: false, repeated: repeated)
-
+ window.eventsHandler.pushEvent onKey,
+ KeyEvent(window: window, key: key, pressed: false, repeated: repeated)
of WmChar, WmSyschar, WmUnichar:
- if window.eventsHandler.onTextInput == nil: return 1 # no need to handle
+ if window.eventsHandler.onTextInput == nil:
+ return 1 # no need to handle
if (window.keyboard.pressed * {lcontrol, rcontrol, lalt, ralt}).len == 0:
let s = %$[wParam.WChar]
if s.len > 0 and s notin ["\u001B"]:
- window.eventsHandler.pushEvent onTextInput, TextInputEvent(window: window, text: s)
-
+ window.eventsHandler.pushEvent onTextInput,
+ TextInputEvent(window: window, text: s)
of WmSetCursor:
if lParam.LoWord == HtClient:
SetCursor window.wcursor
return 1
return window.handle.DefWindowProc(message, wParam, lParam)
-
of WmGetMinMaxInfo:
let info = cast[LpMinMaxInfo](lParam)
if window.m_minSize != ivec2():
@@ -753,44 +896,63 @@ proc poolEvent(window: WindowWinapi, message: Uint, wParam: WParam, lParam: LPar
if window.m_maxSize != ivec2():
info[].ptMaxTrackSize.x = window.m_maxSize.x
info[].ptMaxTrackSize.y = window.m_maxSize.y
-
of WmNcHitTest:
- if window.titleRegion.isNone and window.borderWidth.isNone: return window.handle.DefWindowProc(message, wParam, lParam)
+ if window.titleRegion.isNone and window.borderWidth.isNone:
+ return window.handle.DefWindowProc(message, wParam, lParam)
var pos = Point(x: lParam.GetX_LParam.LONG, y: lParam.GetY_LParam.LONG)
ScreenToClient(window.handle, pos.addr)
let pos_f32 = vec2(pos.x.float32, pos.y.float32)
case window.windowPartAt(pos_f32)
- of WindowPart.title: return HtCaption
- of WindowPart.client: return HtClient
- of WindowPart.border_top_left: return HtTopLeft
- of WindowPart.border_top_right: return HtTopRight
- of WindowPart.border_bottom_left: return HtBottomLeft
- of WindowPart.border_bottom_right: return HtBottomRight
- of WindowPart.border_top: return HtTop
- of WindowPart.border_bottom: return HtBottom
- of WindowPart.border_left: return HtLeft
- of WindowPart.border_right: return HtRight
- of WindowPart.none: return HtTransparent
-
+ of WindowPart.title:
+ return HtCaption
+ of WindowPart.client:
+ return HtClient
+ of WindowPart.border_top_left:
+ return HtTopLeft
+ of WindowPart.border_top_right:
+ return HtTopRight
+ of WindowPart.border_bottom_left:
+ return HtBottomLeft
+ of WindowPart.border_bottom_right:
+ return HtBottomRight
+ of WindowPart.border_top:
+ return HtTop
+ of WindowPart.border_bottom:
+ return HtBottom
+ of WindowPart.border_left:
+ return HtLeft
+ of WindowPart.border_right:
+ return HtRight
+ of WindowPart.none:
+ return HtTransparent
of WmDwmCompositionChanged:
if window.transparent:
window.enableTransparency()
-
- else: return window.handle.DefWindowProc(message, wParam, lParam)
-
+ else:
+ return window.handle.DefWindowProc(message, wParam, lParam)
proc newSoftwareRenderingWindowWinapi*(
- size = ivec2(1280, 720),
- title = "",
- screen = defaultScreenWinapi(),
- resizable = true,
- fullscreen = false,
- frameless = false,
- transparent = false,
+ size = ivec2(1280, 720),
+ title = "",
+ screen = defaultScreenWinapi(),
+ resizable = true,
+ fullscreen = false,
+ frameless = false,
+ transparent = false,
): WindowWinapiSoftwareRendering =
new result
result.initWindow(size, screen, fullscreen, frameless, transparent)
result.title = title
- if not resizable: result.resizable = false
+ if not resizable:
+ result.resizable = false
+
+proc newPopupWindowWinapi*(
+ parent: WindowWinapi, placement: PopupPlacement, transparent = false, grab = true
+): WindowWinapiSoftwareRendering =
+ if parent == nil:
+ raise ValueError.newException("Popup windows require a parent window")
+
+ new result
+ result.initPopupWindow(parent, placement, transparent, grab)
diff --git a/src/siwin/platforms/x11/window.nim b/src/siwin/platforms/x11/window.nim
index 5f93714..cba828a 100644
--- a/src/siwin/platforms/x11/window.nim
+++ b/src/siwin/platforms/x11/window.nim
@@ -386,6 +386,13 @@ proc `=destroy`(x: WindowX11SoftwareRenderingObj) {.siwin_destructor.} =
proc pushEvent[T](event: proc(e: T), args: T) =
if event != nil: event(args)
+proc popupWindowPos(window: WindowX11; placement: PopupPlacement): IVec2 =
+ let parent = window.parentWindow()
+ if parent == nil:
+ placement.popupRelativePos()
+ else:
+ parent.pos + placement.popupRelativePos()
+
proc resizePixelBuffer(window: WindowX11SoftwareRendering, size: IVec2) =
window.pixels = window.pixels.realloc(size.x * size.y * Color32bit.sizeof)
@@ -499,6 +506,21 @@ proc initSoftwareRenderingWindow(
window.gc.gc = window.globals.display.XCreateGC(window.handle, GCForeground or GCBackground, window.gc.gcv.addr)
+proc initPopupWindow(
+ window: WindowX11SoftwareRendering,
+ parent: WindowX11,
+ placement: PopupPlacement,
+ transparent: bool,
+ grab: bool,
+) =
+ let screen = parent.globals.screenX11(parent.screen.int32)
+ window.initPopupState(parent, placement, grab)
+ window.initSoftwareRenderingWindow(placement.popupSize(), screen, false, true, transparent, "")
+ discard window.globals.display.XSetTransientForHint(window.handle, parent.handle)
+ window.m_resizable = false
+ window.pos = window.popupWindowPos(placement)
+
+
method `title=`*(window: WindowX11, v: string) =
discard window.globals.display.XChangeProperty(
window.handle, window.globals.atoms.netWmName, window.globals.atoms.utf8String, 8,
@@ -511,6 +533,11 @@ method `title=`*(window: WindowX11, v: string) =
window.globals.display.Xutf8SetWMProperties(window.handle, v, v, nil, 0, nil, nil, nil)
+method close*(window: WindowX11) =
+ window.notifyPopupDone(PopupDismissReason.pdrClientClosed)
+ procCall window.Window.close()
+
+
method `fullscreen=`*(window: WindowX11, v: bool) =
var event = window.globals.newClientMessage(
window.handle, window.globals.atoms.netWmState, [Atom 2, window.globals.atoms.netWmStateFullscreen]
@@ -561,16 +588,38 @@ method `frameless=`*(window: WindowX11, v: bool) =
method `size=`*(window: WindowX11, v: IVec2) =
+ if window.isPopup:
+ var placement = window.placement
+ placement.size = v
+ window.placement = placement
+ return
if window.fullscreen:
window.fullscreen = false
discard window.globals.display.XResizeWindow(window.handle, v.x.cuint, v.y.cuint)
method `pos=`*(window: WindowX11, v: IVec2) =
+ if window.isPopup:
+ return
if window.m_fullscreen: return
discard window.globals.display.XMoveWindow(window.handle, v.x.cint, v.y.cint)
+method `placement=`*(window: WindowX11, v: PopupPlacement) =
+ window.m_popupPlacement = v
+ let size = v.popupSize()
+ window.m_size = size
+ if window.handle != 0:
+ discard window.globals.display.XResizeWindow(window.handle, size.x.cuint, size.y.cuint)
+ let pos = window.popupWindowPos(v)
+ window.m_pos = pos
+ discard window.globals.display.XMoveWindow(window.handle, pos.x.cint, pos.y.cint)
+
+
+method reposition*(window: WindowX11, v: PopupPlacement) =
+ window.placement = v
+
+
proc setX11Cursor(window: WindowX11, v: Cursor) =
if window.xCursor != 0:
discard window.globals.display.XFreeCursor(window.xCursor)
@@ -1525,5 +1574,21 @@ proc newSoftwareRenderingWindowX11*(
result.title = title
if not resizable: result.resizable = false
+
+proc newPopupWindowX11*(
+ globals: SiwinGlobalsX11,
+ parent: WindowX11,
+ placement: PopupPlacement,
+ transparent = false,
+ grab = true,
+): WindowX11SoftwareRendering =
+ if parent == nil:
+ raise ValueError.newException("Popup windows require a parent window")
+
+ new result
+ result.softwarePresentEnabled = true
+ result.globals = globals
+ result.initPopupWindow(parent, placement, transparent, grab)
+
proc setSoftwarePresentEnabled*(window: WindowX11SoftwareRendering, enabled: bool) =
window.softwarePresentEnabled = enabled
diff --git a/src/siwin/window.nim b/src/siwin/window.nim
index bc7098e..6944c60 100644
--- a/src/siwin/window.nim
+++ b/src/siwin/window.nim
@@ -8,25 +8,20 @@ export anyWindow
when not siwin_use_lib:
when defined(android):
import ./platforms/android/window as androidWindow
-
elif defined(linux) or defined(bsd):
import ./platforms/x11/siwinGlobals as x11SiwinGlobals
import ./platforms/x11/window as x11Window
import ./platforms/wayland/siwinGlobals as waylandSiwinGlobals
import ./platforms/wayland/window as waylandWindow
-
elif defined(windows):
import ./platforms/winapi/window as winapiWindow
-
elif defined(macosx):
import ./platforms/cocoa/window as cocoaWindow
-
when not siwin_use_lib:
proc screenCount*(globals: SiwinGlobals): int32 =
when defined(android):
1
-
elif defined(linux) or defined(bsd):
if globals of SiwinGlobalsX11:
result = globals.SiwinGlobalsX11.screenCountX11()
@@ -34,14 +29,14 @@ when not siwin_use_lib:
result = globals.SiwinGlobalsWayland.screenCountWayland()
else:
raise SiwinPlatformSupportDefect.newException("Unsupported platform")
-
- elif defined(windows): screenCountWinapi()
- elif defined(macosx): screenCountCocoa()
+ elif defined(windows):
+ screenCountWinapi()
+ elif defined(macosx):
+ screenCountCocoa()
proc screen*(globals: SiwinGlobals, number: int32): Screen =
when defined(android):
Screen()
-
elif defined(linux) or defined(bsd):
if globals of SiwinGlobalsX11:
result = globals.SiwinGlobalsX11.screenX11(number)
@@ -49,14 +44,14 @@ when not siwin_use_lib:
result = globals.SiwinGlobalsWayland.screenWayland(number)
else:
raise SiwinPlatformSupportDefect.newException("Unsupported platform")
-
- elif defined(windows): screenWinapi(number)
- elif defined(macosx): screenCocoa(number)
+ elif defined(windows):
+ screenWinapi(number)
+ elif defined(macosx):
+ screenCocoa(number)
proc defaultScreen*(globals: SiwinGlobals): Screen =
when defined(android):
Screen()
-
elif defined(linux) or defined(bsd):
if globals of SiwinGlobalsX11:
result = globals.SiwinGlobalsX11.defaultScreenX11()
@@ -64,91 +59,149 @@ when not siwin_use_lib:
result = globals.SiwinGlobalsWayland.defaultScreenWayland()
else:
raise SiwinPlatformSupportDefect.newException("Unsupported platform")
-
- elif defined(windows): defaultScreenWinapi()
- elif defined(macosx): defaultScreenCocoa()
-
+ elif defined(windows):
+ defaultScreenWinapi()
+ elif defined(macosx):
+ defaultScreenCocoa()
proc newSoftwareRenderingWindow*(
- globals: SiwinGlobals,
- size = ivec2(1280, 720),
- title = "",
- screen: int32 = -1,
- fullscreen = false,
- resizable = true,
- frameless = false,
- transparent = false,
-
- class = "", # window class (used in x11), equals to title if not specified
+ globals: SiwinGlobals,
+ size = ivec2(1280, 720),
+ title = "",
+ screen: int32 = -1,
+ fullscreen = false,
+ resizable = true,
+ frameless = false,
+ transparent = false,
+ class = "", # window class (used in x11), equals to title if not specified
): Window =
when defined(android):
newSoftwareRenderingWindowAndroid(
- size, title,
+ size,
+ title,
# (if screen == -1: defaultScreenAndroid() else: screenAndroid(screen)),
- resizable, fullscreen, frameless, transparent
+ resizable,
+ fullscreen,
+ frameless,
+ transparent,
)
-
elif defined(linux) or defined(bsd):
if globals of SiwinGlobalsX11:
result = globals.SiwinGlobalsX11.newSoftwareRenderingWindowX11(
- size, title,
- (if screen == -1: globals.SiwinGlobalsX11.defaultScreenX11() else: globals.SiwinGlobalsX11.screenX11(screen)),
- resizable, fullscreen, frameless, transparent,
- (if class == "": title else: class)
+ size,
+ title,
+ (
+ if screen == -1: globals.SiwinGlobalsX11.defaultScreenX11()
+ else: globals.SiwinGlobalsX11.screenX11(screen)
+ ),
+ resizable,
+ fullscreen,
+ frameless,
+ transparent,
+ (if class == "": title else: class),
)
elif globals of SiwinGlobalsWayland:
result = globals.SiwinGlobalsWayland.newSoftwareRenderingWindowWayland(
- size, title,
- (if screen == -1: globals.SiwinGlobalsWayland.defaultScreenWayland() else: globals.SiwinGlobalsWayland.screenWayland(screen)),
- resizable, fullscreen, frameless, transparent
+ size,
+ title,
+ (
+ if screen == -1: globals.SiwinGlobalsWayland.defaultScreenWayland()
+ else: globals.SiwinGlobalsWayland.screenWayland(screen)
+ ),
+ resizable,
+ fullscreen,
+ frameless,
+ transparent,
)
else:
raise SiwinPlatformSupportDefect.newException("Unsupported platform")
-
elif defined(windows):
newSoftwareRenderingWindowWinapi(
- size, title,
+ size,
+ title,
(if screen == -1: defaultScreenWinapi() else: screenWinapi(screen)),
- resizable, fullscreen, frameless, transparent
+ resizable,
+ fullscreen,
+ frameless,
+ transparent,
)
-
elif defined(macosx):
newSoftwareRenderingWindowCocoa(
- size, title,
+ size,
+ title,
(if screen == -1: defaultScreenCocoa() else: screenCocoa(screen)),
- resizable, fullscreen, frameless, transparent
+ resizable,
+ fullscreen,
+ frameless,
+ transparent,
)
+ proc newPopupWindow*(
+ globals: SiwinGlobals,
+ parent: Window,
+ placement: PopupPlacement,
+ transparent = false,
+ grab = true,
+ ): PopupWindow =
+ when defined(android):
+ raise SiwinPlatformSupportDefect.newException(
+ "Popup windows are not supported on Android"
+ )
+ elif defined(linux) or defined(bsd):
+ if globals of SiwinGlobalsX11:
+ result = globals.SiwinGlobalsX11.newPopupWindowX11(
+ parent.WindowX11, placement, transparent, grab
+ )
+ elif globals of SiwinGlobalsWayland:
+ result = globals.SiwinGlobalsWayland.newPopupWindowWayland(
+ parent.WindowWayland, placement, transparent, grab
+ )
+ else:
+ raise SiwinPlatformSupportDefect.newException("Unsupported platform")
+ elif defined(windows):
+ result = newPopupWindowWinapi(parent.WindowWinapi, placement, transparent, grab)
+ elif defined(macosx):
+ result = newPopupWindowCocoa(parent.WindowCocoa, placement, transparent, grab)
when defined(android):
proc loadExtensions*() =
discard
+proc siwin_screen_count(globals: SiwinGlobals): cint {.siwin_import_export.} =
+ screenCount(globals)
+proc siwin_get_screen(globals: SiwinGlobals, n: cint): Screen {.siwin_import_export.} =
+ screen(globals, n.int32)
-proc siwin_screen_count(globals: SiwinGlobals): cint {.siwin_import_export.} = screenCount(globals)
-proc siwin_get_screen(globals: SiwinGlobals, n: cint): Screen {.siwin_import_export.} = screen(globals, n.int32)
-proc siwin_default_screen(globals: SiwinGlobals): Screen {.siwin_import_export.} = defaultScreen(globals)
-
+proc siwin_default_screen(globals: SiwinGlobals): Screen {.siwin_import_export.} =
+ defaultScreen(globals)
proc siwin_new_software_rendering_window(
- globals: SiwinGlobals,
- size_x: cint, size_y: cint, title: cstring, screen: cint,
- fullscreen: cchar, resizable: cchar, frameless: cchar, transparent: cchar,
- winclass: cstring
+ globals: SiwinGlobals,
+ size_x: cint,
+ size_y: cint,
+ title: cstring,
+ screen: cint,
+ fullscreen: cchar,
+ resizable: cchar,
+ frameless: cchar,
+ transparent: cchar,
+ winclass: cstring,
): Window {.siwin_import_export.} =
newSoftwareRenderingWindow(
globals,
- ivec2(size_x.int32, size_y.int32), $title, screen.int32,
- fullscreen.bool, resizable.bool, frameless.bool, transparent.bool,
- $winclass
+ ivec2(size_x.int32, size_y.int32),
+ $title,
+ screen.int32,
+ fullscreen.bool,
+ resizable.bool,
+ frameless.bool,
+ transparent.bool,
+ $winclass,
)
-
-
proc screenCount*(globals: SiwinGlobals): int32 {.siwin_export_import.} =
siwin_screen_count(globals).int32
-
proc screen*(globals: SiwinGlobals, number: int32): Screen {.siwin_export_import.} =
siwin_get_screen(globals, n.cint)
@@ -156,143 +209,262 @@ proc screen*(globals: SiwinGlobals, number: int32): Screen {.siwin_export_import
proc defaultScreen*(globals: SiwinGlobals): Screen {.siwin_export_import.} =
siwin_default_screen(globals)
-
proc newSoftwareRenderingWindow*(
- globals: SiwinGlobals,
- size = ivec2(1280, 720),
- title = "",
- screen: int32 = -1,
- fullscreen = false,
- resizable = true,
- frameless = false,
- transparent = false,
-
- class = "", # window class (used in x11), equals to title if not specified
+ globals: SiwinGlobals,
+ size = ivec2(1280, 720),
+ title = "",
+ screen: int32 = -1,
+ fullscreen = false,
+ resizable = true,
+ frameless = false,
+ transparent = false,
+ class = "", # window class (used in x11), equals to title if not specified
): Window {.siwin_export_import.} =
result = siwin_new_software_rendering_window(
- globals,
- size.x, size.y, title.cstring, screen.cint,
- fullscreen.cchar, resizable.cchar, frameless.cchar, transparent.cchar,
- class.cstring,
+ globals, size.x, size.y, title.cstring, screen.cint, fullscreen.cchar,
+ resizable.cchar, frameless.cchar, transparent.cchar, class.cstring,
)
GC_ref(result)
-
proc newSoftwareRenderingWindow*(
- size = ivec2(1280, 720),
- title = "",
- screen: int32 = -1,
- fullscreen = false,
- resizable = true,
- frameless = false,
- transparent = false,
-
- class = "", # window class (used in x11), equals to title if not specified
-
- preferedPlatform: Platform = defaultPreferedPlatform(),
+ size = ivec2(1280, 720),
+ title = "",
+ screen: int32 = -1,
+ fullscreen = false,
+ resizable = true,
+ frameless = false,
+ transparent = false,
+ class = "", # window class (used in x11), equals to title if not specified
+ preferedPlatform: Platform = defaultPreferedPlatform(),
): Window =
- newSoftwareRenderingWindow(newSiwinGlobals(preferedPlatform), size, title, screen, fullscreen, resizable, frameless, transparent, class)
-
+ newSoftwareRenderingWindow(
+ newSiwinGlobals(preferedPlatform),
+ size,
+ title,
+ screen,
+ fullscreen,
+ resizable,
+ frameless,
+ transparent,
+ class,
+ )
when siwin_build_lib:
import std/options
import ./colorutils
import ./platforms/any/[clipboards]
- {.push, exportc, cdecl, dynlib.}
+ {.push exportc, cdecl, dynlib.}
+
+ proc siwin_destroy_window(window: Window) =
+ GC_unref(window)
+
+ proc siwin_screen_number(screen: Screen): cint =
+ screen.number.cint
+
+ proc siwin_sreen_width(screen: Screen): cint =
+ screen.width.cint
+
+ proc siwin_sreen_height(screen: Screen): cint =
+ screen.height.cint
+
+ proc siwin_window_closed(window: Window): cchar =
+ window.closed.cchar
+
+ proc siwin_window_opened(window: Window): cchar =
+ window.opened.cchar
+
+ proc siwin_window_close(window: Window) =
+ close(window)
- proc siwin_destroy_window(window: Window) = GC_unref(window)
+ proc siwin_window_transparent(window: Window): cchar =
+ window.transparent.cchar
- proc siwin_screen_number(screen: Screen): cint = screen.number.cint
- proc siwin_sreen_width(screen: Screen): cint = screen.width.cint
- proc siwin_sreen_height(screen: Screen): cint = screen.height.cint
+ proc siwin_window_frameless(window: Window): cchar =
+ window.frameless.cchar
+
+ proc siwin_window_cursor(window: Window, out_cursor: ptr Cursor) =
+ out_cursor[] = window.cursor
+
+ proc siwin_window_separateTouch(window: Window): cchar =
+ window.separateTouch.cchar
- proc siwin_window_closed(window: Window): cchar = window.closed.cchar
- proc siwin_window_opened(window: Window): cchar = window.opened.cchar
- proc siwin_window_close(window: Window) = close(window)
- proc siwin_window_transparent(window: Window): cchar = window.transparent.cchar
- proc siwin_window_frameless(window: Window): cchar = window.frameless.cchar
- proc siwin_window_cursor(window: Window, out_cursor: ptr Cursor) = out_cursor[] = window.cursor
- proc siwin_window_separateTouch(window: Window): cchar = window.separateTouch.cchar
-
proc siwin_window_size(window: Window, out_size_x, out_size_y: ptr cint) =
let size = window.size
out_size_x[] = size.x.cint
out_size_y[] = size.y.cint
-
+
proc siwin_window_pos(window: Window, out_pos_x, out_pos_y: ptr cint) =
let pos = window.pos
out_pos_x[] = pos.x.cint
out_pos_y[] = pos.y.cint
-
- proc siwin_window_fullscreen(window: Window): cchar = window.fullscreen.cchar
- proc siwin_window_maximized(window: Window): cchar = window.maximized.cchar
- proc siwin_window_minimized(window: Window): cchar = window.minimized.cchar
- proc siwin_window_visible(window: Window): cchar = window.visible.cchar
- proc siwin_window_resizable(window: Window): cchar = window.resizable.cchar
-
+
+ proc siwin_window_fullscreen(window: Window): cchar =
+ window.fullscreen.cchar
+
+ proc siwin_window_maximized(window: Window): cchar =
+ window.maximized.cchar
+
+ proc siwin_window_minimized(window: Window): cchar =
+ window.minimized.cchar
+
+ proc siwin_window_visible(window: Window): cchar =
+ window.visible.cchar
+
+ proc siwin_window_resizable(window: Window): cchar =
+ window.resizable.cchar
+
proc siwin_window_minSize(window: Window, out_minSize_x, out_minSize_y: ptr cint) =
let minSize = window.minSize
out_minSize_x[] = minSize.x.cint
out_minSize_y[] = minSize.y.cint
-
+
proc siwin_window_maxSize(window: Window, out_maxSize_x, out_maxSize_y: ptr cint) =
let maxSize = window.maxSize
out_maxSize_x[] = maxSize.x.cint
out_maxSize_y[] = maxSize.y.cint
-
- proc siwin_window_focused(window: Window): cchar = window.focused.cchar
- proc siwin_window_redraw(window: Window) = window.redraw()
- proc siwin_window_set_frameless(window: Window, v: cchar) = window.frameless = v.bool
- proc siwin_window_set_cursor(window: Window, v: ptr Cursor) = window.cursor = v[]
- proc siwin_window_set_separate_touch(window: Window, v: cchar) = window.separateTouch = v.bool
- proc siwin_window_set_size(window: Window, size_x, size_y: cint) = window.size = ivec2(size_x.int32, size_y.int32)
- proc siwin_window_set_pos(window: Window, pos_x, pos_y: cint) = window.pos = ivec2(pos_x.int32, pos_y.int32)
- proc siwin_window_set_title(window: Window, v: cstring) = window.title = $v
- proc siwin_window_set_fullscreen(window: Window, v: cchar) = window.fullscreen = v.bool
- proc siwin_window_set_maximized(window: Window, v: cchar) = window.maximized = v.bool
- proc siwin_window_set_minimized(window: Window, v: cchar) = window.minimized = v.bool
- proc siwin_window_set_visible(window: Window, v: cchar) = window.visible = v.bool
- proc siwin_window_set_resizable(window: Window, v: cchar) = window.resizable = v.bool
- proc siwin_window_set_min_size(window: Window, v_x, v_y: cint) = window.minSize = ivec2(v_x.int32, v_y.int32)
- proc siwin_window_set_max_size(window: Window, v_x, v_y: cint) = window.maxSize = ivec2(v_x.int32, v_y.int32)
- proc siwin_window_clear_icon(window: Window) = window.icon = nil
- proc siwin_window_set_icon(window: Window, v: ptr PixelBuffer) = window.icon = v[]
-
- proc siwin_window_start_interactive_move(window: Window, has_pos: cchar, pos_x, pos_y: cfloat) =
- window.startInteractiveMove(if has_pos.bool: some vec2(pos_x.float32, pos_y.float32) else: none Vec2)
-
- proc siwin_window_start_interactive_resize(window: Window, edge: Edge, has_pos: cchar, pos_x, pos_y: cfloat) =
- window.startInteractiveResize(edge, if has_pos.bool: some vec2(pos_x.float32, pos_y.float32) else: none Vec2)
-
- proc siwin_window_show_window_menu(window: Window, has_pos: cchar, pos_x, pos_y: cfloat) =
- window.showWindowMenu(if has_pos.bool: some vec2(pos_x.float32, pos_y.float32) else: none Vec2)
-
- proc siwin_window_set_input_region(window: Window, pos_x, pos_y, size_x, size_y: cfloat) =
- window.setInputRegion(vec2(pos_x.float32, pos_y.float32), vec2(size_x.float32, size_y.float32))
-
- proc siwin_window_set_title_region(window: Window, pos_x, pos_y, size_x, size_y: cfloat) =
- window.setTitleRegion(vec2(pos_x.float32, pos_y.float32), vec2(size_x.float32, size_y.float32))
-
- proc siwin_window_set_border_width(window: Window, innerWidth, outerWidth: cfloat, diagonalSize: cfloat) =
+
+ proc siwin_window_focused(window: Window): cchar =
+ window.focused.cchar
+
+ proc siwin_window_redraw(window: Window) =
+ window.redraw()
+
+ proc siwin_window_set_frameless(window: Window, v: cchar) =
+ window.frameless = v.bool
+
+ proc siwin_window_set_cursor(window: Window, v: ptr Cursor) =
+ window.cursor = v[]
+
+ proc siwin_window_set_separate_touch(window: Window, v: cchar) =
+ window.separateTouch = v.bool
+
+ proc siwin_window_set_size(window: Window, size_x, size_y: cint) =
+ window.size = ivec2(size_x.int32, size_y.int32)
+
+ proc siwin_window_set_pos(window: Window, pos_x, pos_y: cint) =
+ window.pos = ivec2(pos_x.int32, pos_y.int32)
+
+ proc siwin_window_set_title(window: Window, v: cstring) =
+ window.title = $v
+
+ proc siwin_window_set_fullscreen(window: Window, v: cchar) =
+ window.fullscreen = v.bool
+
+ proc siwin_window_set_maximized(window: Window, v: cchar) =
+ window.maximized = v.bool
+
+ proc siwin_window_set_minimized(window: Window, v: cchar) =
+ window.minimized = v.bool
+
+ proc siwin_window_set_visible(window: Window, v: cchar) =
+ window.visible = v.bool
+
+ proc siwin_window_set_resizable(window: Window, v: cchar) =
+ window.resizable = v.bool
+
+ proc siwin_window_set_min_size(window: Window, v_x, v_y: cint) =
+ window.minSize = ivec2(v_x.int32, v_y.int32)
+
+ proc siwin_window_set_max_size(window: Window, v_x, v_y: cint) =
+ window.maxSize = ivec2(v_x.int32, v_y.int32)
+
+ proc siwin_window_clear_icon(window: Window) =
+ window.icon = nil
+
+ proc siwin_window_set_icon(window: Window, v: ptr PixelBuffer) =
+ window.icon = v[]
+
+ proc siwin_window_start_interactive_move(
+ window: Window, has_pos: cchar, pos_x, pos_y: cfloat
+ ) =
+ window.startInteractiveMove(
+ if has_pos.bool:
+ some vec2(pos_x.float32, pos_y.float32)
+ else:
+ none Vec2
+ )
+
+ proc siwin_window_start_interactive_resize(
+ window: Window, edge: Edge, has_pos: cchar, pos_x, pos_y: cfloat
+ ) =
+ window.startInteractiveResize(
+ edge,
+ if has_pos.bool:
+ some vec2(pos_x.float32, pos_y.float32)
+ else:
+ none Vec2
+ ,
+ )
+
+ proc siwin_window_show_window_menu(
+ window: Window, has_pos: cchar, pos_x, pos_y: cfloat
+ ) =
+ window.showWindowMenu(
+ if has_pos.bool:
+ some vec2(pos_x.float32, pos_y.float32)
+ else:
+ none Vec2
+ )
+
+ proc siwin_window_set_input_region(
+ window: Window, pos_x, pos_y, size_x, size_y: cfloat
+ ) =
+ window.setInputRegion(
+ vec2(pos_x.float32, pos_y.float32), vec2(size_x.float32, size_y.float32)
+ )
+
+ proc siwin_window_set_title_region(
+ window: Window, pos_x, pos_y, size_x, size_y: cfloat
+ ) =
+ window.setTitleRegion(
+ vec2(pos_x.float32, pos_y.float32), vec2(size_x.float32, size_y.float32)
+ )
+
+ proc siwin_window_set_border_width(
+ window: Window, innerWidth, outerWidth: cfloat, diagonalSize: cfloat
+ ) =
window.setBorderWidth(innerWidth.float32, outerWidth.float32, diagonalSize.float32)
- proc siwin_window_pixel_buffer(window: Window, out_buffer: ptr PixelBuffer) = out_buffer[] = window.pixelBuffer
- proc siwin_window_make_current(window: Window) = window.makeCurrent()
-
+ proc siwin_window_pixel_buffer(window: Window, out_buffer: ptr PixelBuffer) =
+ out_buffer[] = window.pixelBuffer
+
+ proc siwin_window_make_current(window: Window) =
+ window.makeCurrent()
+
proc siwin_window_set_vsync(window: Window, v: cchar): cchar =
- try: window.`vsync=`(v.bool, false)
- except: return 1.cchar
-
- proc siwin_window_vulkan_surface(window: Window): pointer = window.vulkanSurface
- proc siwin_window_clipboard(window: Window): Clipboard = window.clipboard
- proc siwin_window_selection_clipboard(window: Window): Clipboard = window.selectionClipboard
- proc siwin_window_dragndrop_clipboard(window: Window): Clipboard = window.dragndropClipboard
- proc siwin_window_set_drag_status(window: Window, v: DragStatus) = window.dragStatus = v
- proc siwin_window_first_step(window: Window, makeVisible: cchar) = window.firstStep(makeVisible.bool)
- proc siwin_window_step(window: Window) = window.step()
- proc siwin_window_run(window: Window, makeVisible: cchar) = window.run(makeVisible.bool)
-
- proc siwin_window_set_event_handler(window: Window, eventHandler: ptr WindowEventsHandler) = window.eventsHandler = eventHandler[]
+ try:
+ window.`vsync=`(v.bool, false)
+ except:
+ return 1.cchar
+
+ proc siwin_window_vulkan_surface(window: Window): pointer =
+ window.vulkanSurface
+
+ proc siwin_window_clipboard(window: Window): Clipboard =
+ window.clipboard
+
+ proc siwin_window_selection_clipboard(window: Window): Clipboard =
+ window.selectionClipboard
+
+ proc siwin_window_dragndrop_clipboard(window: Window): Clipboard =
+ window.dragndropClipboard
+
+ proc siwin_window_set_drag_status(window: Window, v: DragStatus) =
+ window.dragStatus = v
+
+ proc siwin_window_first_step(window: Window, makeVisible: cchar) =
+ window.firstStep(makeVisible.bool)
+
+ proc siwin_window_step(window: Window) =
+ window.step()
+
+ proc siwin_window_run(window: Window, makeVisible: cchar) =
+ window.run(makeVisible.bool)
+
+ proc siwin_window_set_event_handler(
+ window: Window, eventHandler: ptr WindowEventsHandler
+ ) =
+ window.eventsHandler = eventHandler[]
{.pop.}