├── .github
└── workflows
│ └── test.yml
├── examples
├── config.nims
├── example_01_basic_app.nim
├── example_01_basic_app.png
├── example_02_controls.nim
├── example_03_layout_container.nim
├── example_04_msgboxes.nim
├── example_05_handle_window_close.nim
├── example_06_keyboard_events.nim
├── example_08_timers.nim
├── example_09_font_size.nim
├── example_10_drawing.nim
├── example_11_image_processing_cli.nim
├── example_12_file_dialogs.nim
├── example_13_fixed_layout.nim
├── example_14_container_scrolling.nim
├── example_15_threading.nim
├── example_30_custom_button.nim
├── example_31_custom_widget.nim
├── example_90_widget_error.nim
├── example_91_unhandled_exception.nim
└── nim.cfg
├── faq.md
├── license.md
├── nigui.nimble
├── readme.md
├── screenshots.png
└── src
├── nigui.nim
└── nigui
├── msgbox.nim
└── private
├── gtk3
├── gtk3.nim
├── platform_impl.nim
├── platform_types1.nim
├── platform_types2.nim
└── platform_types3.nim
└── windows
├── platform_impl.nim
├── platform_types1.nim
├── platform_types2.nim
├── platform_types3.nim
├── res
├── amd64.o
├── i386.o
├── manifest.xml
└── resource.rc
└── windows.nim
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | jobs:
8 | skip:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - run: echo "Skip job"
12 |
13 | before:
14 | runs-on: ubuntu-latest
15 | if: "! contains(github.event.head_commit.message, '[skip ci]')"
16 | steps:
17 | - run: echo "not contains '[skip ci]'"
18 |
19 | test:
20 | runs-on: ${{ matrix.os }}
21 | strategy:
22 | matrix:
23 | os:
24 | - ubuntu-latest
25 | - macOS-latest
26 | - windows-latest
27 | nim_version:
28 | - '1.2.0'
29 | - 'stable'
30 | needs: before
31 | steps:
32 | - uses: actions/checkout@v2
33 |
34 | - name: Set cache-key
35 | id: vars
36 | run: |
37 | if [[ ${{ matrix.nim_version }} == stable ]]; then
38 | echo ::set-output name=cache-key::$(date +%Y-%m-%d)
39 | else
40 | echo ::set-output name=cache-key::${{ matrix.nim_version }}
41 | fi
42 | shell: bash
43 |
44 | - name: Print cache-key
45 | run: echo cache-key = ${{ steps.vars.outputs.cache-key }}
46 |
47 | - name: Cache choosenim
48 | id: cache-choosenim
49 | uses: actions/cache@v1
50 | with:
51 | path: ~/.choosenim
52 | key: ${{ runner.os }}-choosenim-${{ steps.vars.outputs.cache-key }}
53 | if: runner.os != 'Windows'
54 | - name: Cache nimble
55 | id: cache-nimble
56 | uses: actions/cache@v1
57 | with:
58 | path: ~/.nimble
59 | key: ${{ runner.os }}-nimble-${{ hashFiles('*.nimble') }}
60 | if: runner.os != 'Windows'
61 | - uses: jiro4989/setup-nim-action@v1
62 | with:
63 | nim-version: ${{ matrix.nim_version }}
64 |
65 | - name: Test examples
66 | working-directory: examples
67 | run: |
68 | for file in $(ls -v example_*nim); do
69 | echo "=== test: $file ==="
70 | nim c "$file"
71 | echo ""
72 | done
73 | shell: bash
74 |
75 | mingw:
76 | runs-on: ubuntu-latest
77 | needs: before
78 | steps:
79 | - name: Install MinGW
80 | run: sudo apt install mingw-w64
81 |
82 | - uses: actions/checkout@v2
83 |
84 | - name: Set cache-key
85 | id: vars
86 | run: echo ::set-output name=cache-key::$(date +%Y-%m-%d)
87 |
88 | - name: Print cache-key
89 | run: echo cache-key = ${{ steps.vars.outputs.cache-key }}
90 |
91 | - name: Cache choosenim
92 | id: cache-choosenim
93 | uses: actions/cache@v1
94 | with:
95 | path: ~/.choosenim
96 | key: ${{ runner.os }}-choosenim-${{ steps.vars.outputs.cache-key }}
97 |
98 | - name: Cache nimble
99 | id: cache-nimble
100 | uses: actions/cache@v1
101 | with:
102 | path: ~/.nimble
103 | key: ${{ runner.os }}-nimble-${{ hashFiles('*.nimble') }}
104 |
105 | - uses: jiro4989/setup-nim-action@v1
106 |
107 | - name: Dump versions
108 | run: |
109 | nim -v
110 | nimble -v
111 | - name: Test examples
112 | working-directory: examples
113 | run: |
114 | for cpu in amd64 i386
115 | do
116 | for file in $(ls -v example_*nim)
117 | do
118 | echo "=== test: $file @$cpu ==="
119 | nim c -d:mingw -d:release --cpu:$cpu "$file"
120 | echo ""
121 | done
122 | done
123 |
--------------------------------------------------------------------------------
/examples/config.nims:
--------------------------------------------------------------------------------
1 | switch("path", "$projectDir/../src")
--------------------------------------------------------------------------------
/examples/example_01_basic_app.nim:
--------------------------------------------------------------------------------
1 | # This example shows the basic use of the NiGui toolkit.
2 |
3 | import nigui
4 | # First, import the library.
5 |
6 | app.init()
7 | # Initialization is mandatory.
8 |
9 | var window = newWindow("NiGui Example")
10 | # Create a window with a given title:
11 | # By default, a window is empty and not visible.
12 | # It is played at the center of the screen.
13 | # A window can contain only one control.
14 | # A container can contain multiple controls.
15 |
16 | window.width = 600.scaleToDpi
17 | window.height = 400.scaleToDpi
18 | # Set the size of the window
19 |
20 | # window.iconPath = "example_01_basic_app.png"
21 | # The window icon can be specified this way.
22 | # The default value is the name of the executable file without extension + ".png"
23 |
24 | var container = newLayoutContainer(Layout_Vertical)
25 | # Create a container for controls.
26 | # By default, a container is empty.
27 | # It's size will adapt to it's child controls.
28 | # A LayoutContainer will automatically align the child controls.
29 | # The layout is set to clhorizontal.
30 |
31 | window.add(container)
32 | # Add the container to the window.
33 |
34 | var button = newButton("Button 1")
35 | # Create a button with a given title.
36 |
37 | container.add(button)
38 | # Add the button to the container.
39 |
40 | var textArea = newTextArea()
41 | # Create a multiline text box.
42 | # By default, a text area is empty and editable.
43 |
44 | container.add(textArea)
45 | # Add the text area to the container.
46 |
47 | button.onClick = proc(event: ClickEvent) =
48 | # Set an event handler for the "onClick" event (here as anonymous proc).
49 |
50 | textArea.addLine("Button 1 clicked, message box opened.")
51 | window.alert("This is a simple message box.")
52 | textArea.addLine("Message box closed.")
53 |
54 | window.show()
55 | # Make the window visible on the screen.
56 | # Controls (containers, buttons, ..) are visible by default.
57 |
58 | app.run()
59 | # At last, run the main loop.
60 | # This processes incoming events until the application quits.
61 | # To quit the application, dispose all windows or call "app.quit()".
62 |
--------------------------------------------------------------------------------
/examples/example_01_basic_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simonkrauter/NiGui/f8167a82517973d7cd47797801f90b06360e3f32/examples/example_01_basic_app.png
--------------------------------------------------------------------------------
/examples/example_02_controls.nim:
--------------------------------------------------------------------------------
1 | # This example shows several controls of NiGui.
2 |
3 | import nigui
4 |
5 | app.init()
6 |
7 | var window = newWindow()
8 |
9 | var container = newLayoutContainer(Layout_Vertical)
10 | window.add(container)
11 |
12 | # Add a Button control:
13 | var button = newButton("Button")
14 | container.add(button)
15 |
16 | # Add a Checkbox control:
17 | var checkbox = newCheckbox("Checkbox")
18 | container.add(checkbox)
19 |
20 | # Add a ComboBox control:
21 | var comboBox = newComboBox(@["Option 1", "Option 2"])
22 | container.add(comboBox)
23 |
24 | # Add a Label control:
25 | var label = newLabel("Label")
26 | container.add(label)
27 |
28 | # Add a Progress Bar control:
29 | var progressBar = newProgressBar()
30 | progressBar.value = 0.5
31 | container.add(progressBar)
32 |
33 | # Add a TextBox control:
34 | var textBox = newTextBox("TextBox")
35 | container.add(textBox)
36 |
37 | # Add a TextArea control:
38 | var textArea = newTextArea("TextArea\pLine 2\p")
39 | container.add(textArea)
40 |
41 | # Add more text to the TextArea:
42 | for i in 3..15:
43 | textArea.addLine("Line " & $i)
44 |
45 | # Add click event to the button:
46 | button.onClick = proc(event: ClickEvent) =
47 | textArea.addLine("Button clicked")
48 |
49 | window.show()
50 |
51 | app.run()
52 |
--------------------------------------------------------------------------------
/examples/example_03_layout_container.nim:
--------------------------------------------------------------------------------
1 | # This example shows some possibilities to align controls with a LayoutContainer.
2 |
3 | import nigui
4 |
5 | app.init()
6 |
7 | # Row 1: Auto-sized:
8 | var innerContainer1 = newLayoutContainer(Layout_Horizontal)
9 | innerContainer1.frame = newFrame("Row 1: Auto-sized")
10 | for i in 1..3:
11 | var control = newButton("Button " & $i)
12 | innerContainer1.add(control)
13 |
14 | # Row 2: Auto-sized, more padding:
15 | var innerContainer2 = newLayoutContainer(Layout_Horizontal)
16 | innerContainer2.padding = 10
17 | innerContainer2.frame = newFrame("Row 2: Auto-sized, more padding")
18 | for i in 1..3:
19 | var control = newButton("Button " & $i)
20 | innerContainer2.add(control)
21 |
22 | # Row 3: Auto-sized, more spacing:
23 | var innerContainer3 = newLayoutContainer(Layout_Horizontal)
24 | innerContainer3.spacing = 15
25 | innerContainer3.frame = newFrame("Row 3: Auto-sized, more spacing")
26 | for i in 1..3:
27 | var control = newButton("Button " & $i)
28 | innerContainer3.add(control)
29 |
30 | # Row 4: Controls with WidthMode_Fill:
31 | var innerContainer4 = newLayoutContainer(Layout_Horizontal)
32 | innerContainer4.frame = newFrame("Row 4: Controls with WidthMode_Fill")
33 | innerContainer4.width = 600
34 | for i in 1..3:
35 | var control = newButton("Button " & $i)
36 | control.widthMode = WidthMode_Fill
37 | innerContainer4.add(control)
38 |
39 | # Row 5: Controls with WidthMode_Expand:
40 | var innerContainer5 = newLayoutContainer(Layout_Horizontal)
41 | innerContainer5.frame = newFrame("Row 5: Controls with WidthMode_Expand")
42 | for i in 1..3:
43 | var control = newButton("Button " & $i)
44 | control.widthMode = WidthMode_Expand
45 | innerContainer5.add(control)
46 |
47 | # Row 6: Controls centered:
48 | var innerContainer6 = newLayoutContainer(Layout_Horizontal)
49 | innerContainer6.widthMode = WidthMode_Expand
50 | innerContainer6.height = 80 # problem
51 | innerContainer6.xAlign = XAlign_Center
52 | innerContainer6.yAlign = YAlign_Center
53 | innerContainer6.frame = newFrame("Row 6: Controls centered")
54 | for i in 1..3:
55 | var control = newButton("Button " & $i)
56 | innerContainer6.add(control)
57 |
58 | # Row 7: Container expanded, spread:
59 | var innerContainer7 = newLayoutContainer(Layout_Horizontal)
60 | # innerContainer.height = 80
61 | innerContainer7.widthMode = WidthMode_Expand
62 | innerContainer7.xAlign = XAlign_Spread
63 | innerContainer7.frame = newFrame("Row 7: Container expanded, spread")
64 | for i in 1..3:
65 | var control = newButton("Button " & $i)
66 | innerContainer7.add(control)
67 |
68 | # Row 8: Static size:
69 | var innerContainer8 = newLayoutContainer(Layout_Horizontal)
70 | innerContainer8.widthMode = WidthMode_Expand
71 | innerContainer8.xAlign = XAlign_Center
72 | innerContainer8.yAlign = YAlign_Center
73 | innerContainer8.frame = newFrame("Row 8: Static size")
74 | for i in 1..3:
75 | var control = newButton("Button " & $i)
76 | control.width = 90 * i
77 | control.height = 15 * i
78 | innerContainer8.add(control)
79 |
80 | var mainContainer = newLayoutContainer(Layout_Vertical)
81 | mainContainer.add(innerContainer1)
82 | mainContainer.add(innerContainer2)
83 | mainContainer.add(innerContainer3)
84 | mainContainer.add(innerContainer4)
85 | mainContainer.add(innerContainer5)
86 | mainContainer.add(innerContainer6)
87 | mainContainer.add(innerContainer7)
88 | mainContainer.add(innerContainer8)
89 |
90 | var window = newWindow()
91 | window.width = 800
92 | window.height = 600
93 | window.add(mainContainer)
94 | window.show()
95 |
96 | app.run()
97 |
--------------------------------------------------------------------------------
/examples/example_04_msgboxes.nim:
--------------------------------------------------------------------------------
1 | # This example shows different methods to create message boxes.
2 |
3 | import nigui
4 | import nigui/msgbox
5 |
6 | app.init()
7 |
8 | var window = newWindow()
9 | var mainContainer = newLayoutContainer(Layout_Vertical)
10 | window.add(mainContainer)
11 |
12 | var buttons = newLayoutContainer(Layout_Horizontal)
13 | mainContainer.add(buttons)
14 |
15 | var textArea = newTextArea()
16 | mainContainer.add(textArea)
17 |
18 | var button1 = newButton("Example 1")
19 | buttons.add(button1)
20 | button1.onClick = proc(event: ClickEvent) =
21 | window.alert("Hello.\n\nThis message box is created with \"alert()\".")
22 | textArea.addLine("Message box closed")
23 |
24 | var button2 = newButton("Example 2")
25 | buttons.add(button2)
26 | button2.onClick = proc(event: ClickEvent) =
27 | let res = window.msgBox("Hello.\n\nThis message box is created with \"msgBox()\".")
28 | textArea.addLine("Message box closed, result = " & $res)
29 |
30 | var button3 = newButton("Example 3")
31 | buttons.add(button3)
32 | button3.onClick = proc(event: ClickEvent) =
33 | let res = window.msgBox("Hello.\n\nThis message box is created with \"msgBox()\" and has three buttons.", "Title of message box", "Button 1", "Button 2", "Button 3")
34 | textArea.addLine("Message box closed, result = " & $res)
35 |
36 | window.show()
37 | app.run()
38 |
--------------------------------------------------------------------------------
/examples/example_05_handle_window_close.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to handle a window close click.
2 |
3 | import nigui
4 | import nigui/msgbox
5 |
6 | app.init()
7 |
8 | var window = newWindow()
9 |
10 | window.onCloseClick = proc(event: CloseClickEvent) =
11 | case window.msgBox("Do you want to quit?", "Quit?", "Quit", "Minimize", "Cancel")
12 | of 1: window.dispose()
13 | of 2: window.minimize()
14 | else: discard
15 |
16 | window.show()
17 | app.run()
18 |
--------------------------------------------------------------------------------
/examples/example_06_keyboard_events.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to handle keyboard events.
2 |
3 | import nigui
4 |
5 | app.init()
6 |
7 | var window = newWindow()
8 |
9 | var container = newLayoutContainer(Layout_Vertical)
10 | window.add(container)
11 |
12 | var textBox = newTextBox()
13 | container.add(textBox)
14 |
15 | var label = newLabel()
16 |
17 |
18 | window.onKeyDown = proc(event: KeyboardEvent) =
19 | label.text = label.text & "Window KeyDown event: key: " & $event.key & ", unicode: " & $event.unicode & ", character: " & event.character & ", down keys: " & $downKeys() & "\n"
20 |
21 | # Ctrl + Q -> Quit application
22 | if Key_Q.isDown() and Key_ControlL.isDown():
23 | app.quit()
24 |
25 | textBox.onKeyDown = proc(event: KeyboardEvent) =
26 | label.text = label.text & "TextBox KeyDown event: key: " & $event.key & ", unicode: " & $event.unicode & ", character: " & event.character & ", down keys: " & $downKeys() & "\n"
27 |
28 | # Accept only digits
29 | if event.character.len > 0 and event.character[0].ord >= 32 and (event.character.len != 1 or event.character[0] notin '0'..'9'):
30 | event.handled = true
31 |
32 |
33 | container.add(label)
34 |
35 | window.show()
36 | textBox.focus()
37 |
38 | app.run()
39 |
--------------------------------------------------------------------------------
/examples/example_08_timers.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to use timers.
2 |
3 | import nigui
4 |
5 | app.init()
6 |
7 | var window = newWindow()
8 |
9 | var mainContainer = newLayoutContainer(Layout_Vertical)
10 | window.add(mainContainer)
11 | var buttonContainer = newLayoutContainer(Layout_Horizontal)
12 | mainContainer.add(buttonContainer)
13 |
14 | var textArea = newTextArea()
15 | mainContainer.add(textArea)
16 |
17 | var timer: Timer
18 | var counter = 1
19 |
20 | proc timerProc(event: TimerEvent) =
21 | textArea.addLine($counter)
22 | counter.inc()
23 |
24 | var button1 = newButton("startTimer()")
25 | buttonContainer.add(button1)
26 | button1.onClick = proc(event: ClickEvent) =
27 | timer = startTimer(500, timerProc)
28 |
29 | var button2 = newButton("startRepeatingTimer()")
30 | buttonContainer.add(button2)
31 | button2.onClick = proc(event: ClickEvent) =
32 | timer = startRepeatingTimer(500, timerProc)
33 |
34 | var button3 = newButton("stopTimer()")
35 | buttonContainer.add(button3)
36 | button3.onClick = proc(event: ClickEvent) =
37 | timer.stop()
38 |
39 | window.show()
40 |
41 | app.run()
42 |
--------------------------------------------------------------------------------
/examples/example_09_font_size.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to change the font size of controls.
2 |
3 | import nigui
4 |
5 | app.init()
6 |
7 | var window = newWindow()
8 | window.width = 500
9 | window.height = 600
10 | var container = newLayoutContainer(Layout_Vertical)
11 | window.add(container)
12 |
13 | for i in 12..20:
14 | var innerContainer = newLayoutContainer(Layout_Horizontal)
15 | container.add(innerContainer)
16 | innerContainer.frame = newFrame("Font size: " & $i)
17 | var button = newButton("Button")
18 | button.fontSize = i.float
19 | innerContainer.add(button)
20 | var label = newLabel("Label")
21 | label.fontSize = i.float
22 | innerContainer.add(label)
23 |
24 | window.show()
25 | app.run()
26 |
--------------------------------------------------------------------------------
/examples/example_10_drawing.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to draw the surface of a control.
2 |
3 | import nigui
4 |
5 | app.init()
6 | var window = newWindow()
7 | window.width = 500
8 | window.height = 500
9 |
10 | var control1 = newControl()
11 | window.add(control1)
12 | # Creates a drawable control
13 |
14 | control1.widthMode = WidthMode_Fill
15 | control1.heightMode = HeightMode_Fill
16 | # Let it fill out the whole window
17 |
18 | var image1 = newImage()
19 | image1.loadFromFile("example_01_basic_app.png")
20 | # Reads the file and holds the image as bitmap in memory
21 |
22 | var image2 = newImage()
23 | image2.resize(2, 2)
24 | # Creates a new bitmap
25 |
26 | image2.canvas.setPixel(0, 0, rgb(255, 0, 0))
27 | image2.canvas.setPixel(0, 1, rgb(255, 0, 0))
28 | image2.canvas.setPixel(1, 1, rgb(0, 255, 0))
29 | image2.canvas.setPixel(1, 0, rgb(0, 0, 255))
30 | # Modifies single pixels
31 |
32 | control1.onDraw = proc (event: DrawEvent) =
33 | let canvas = event.control.canvas
34 | # A shortcut
35 |
36 | canvas.areaColor = rgb(30, 30, 30) # dark grey
37 | canvas.fill()
38 | # Fill the whole area
39 |
40 | canvas.setPixel(0, 0, rgb(255, 0, 0))
41 | # Modifies a single pixel
42 |
43 | canvas.areaColor = rgb(255, 0, 0) # red
44 | canvas.drawRectArea(10, 10, 30, 30)
45 | # Draws a filled rectangle
46 |
47 | canvas.lineColor = rgb(255, 0, 0) # red
48 | canvas.drawLine(60, 10, 110, 40)
49 | # Draws a line
50 |
51 | let text = "Hello World!"
52 | canvas.textColor = rgb(0, 255, 0) # lime
53 | canvas.fontSize = 20
54 | canvas.fontFamily = "Arial"
55 | canvas.drawText(text, 10, 70)
56 | # Outputs a text
57 |
58 | canvas.drawRectOutline(10, 70, canvas.getTextWidth(text), canvas.getTextLineHeight())
59 | # Draws a rectangle outline
60 |
61 | canvas.drawImage(image1, 10, 120)
62 | # Draws an image in original size
63 |
64 | canvas.drawImage(image2, 120, 120, 50)
65 | # Draws an image stretched
66 |
67 | control1.onMouseButtonDown = proc (event: MouseEvent) =
68 | echo(event.button, " (", event.x, ", ", event.y, ")")
69 | # Shows where the mouse is clicked in control-relative coordinates
70 |
71 | window.show()
72 | app.run()
73 |
--------------------------------------------------------------------------------
/examples/example_11_image_processing_cli.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to do image processing without GUI
2 |
3 | import nigui
4 |
5 | app.init()
6 |
7 | var image1 = newImage()
8 | image1.loadFromFile("example_01_basic_app.png")
9 | # Reads the file and holds the image as bitmap in memory
10 |
11 | var image2 = newImage()
12 | image2.resize(200, 200)
13 |
14 | let canvas = image2.canvas
15 | # A shortcut
16 |
17 | canvas.areaColor = rgb(30, 30, 30) # dark grey
18 | canvas.fill()
19 | # Fill the whole area
20 |
21 | canvas.setPixel(0, 0, rgb(255, 0, 0))
22 | # Modifies a single pixel
23 |
24 | canvas.areaColor = rgb(255, 0, 0) # red
25 | canvas.drawRectArea(10, 10, 30, 30)
26 | # Draws a filled rectangle
27 |
28 | canvas.lineColor = rgb(255, 0, 0) # red
29 | canvas.drawLine(60, 10, 110, 40)
30 | # Draws a line
31 |
32 | let text = "Hello World!"
33 | canvas.textColor = rgb(0, 255, 0) # lime
34 | canvas.fontSize = 20
35 | canvas.fontFamily = "Arial"
36 | canvas.drawText(text, 10, 70)
37 | # Outputs a text
38 |
39 | canvas.drawRectOutline(10, 70, canvas.getTextWidth(text), canvas.getTextLineHeight())
40 | # Draws a rectangle outline
41 |
42 | canvas.drawImage(image1, 10, 120)
43 | # Draws an image in original size
44 |
45 | image2.saveToPngFile("out.png")
46 | # Save the image as PNG file
47 |
48 |
--------------------------------------------------------------------------------
/examples/example_12_file_dialogs.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to use the Open File and Save File As dialogs.
2 |
3 | import nigui
4 |
5 | app.init()
6 |
7 | var window = newWindow()
8 | var mainContainer = newLayoutContainer(Layout_Vertical)
9 | window.add(mainContainer)
10 |
11 | var buttons = newLayoutContainer(Layout_Horizontal)
12 | mainContainer.add(buttons)
13 |
14 | var textArea = newTextArea()
15 | mainContainer.add(textArea)
16 |
17 | var button1 = newButton("Open ...")
18 | buttons.add(button1)
19 | button1.onClick = proc(event: ClickEvent) =
20 | var dialog = newOpenFileDialog()
21 | dialog.title = "Test Open"
22 | dialog.multiple = true
23 | # dialog.directory = ""
24 | dialog.run()
25 | textArea.addLine($dialog.files.len & " files selected")
26 | if dialog.files.len > 0:
27 | for file in dialog.files:
28 | textArea.addLine(file)
29 |
30 | var button2 = newButton("Save as ...")
31 | buttons.add(button2)
32 | button2.onClick = proc(event: ClickEvent) =
33 | var dialog = SaveFileDialog()
34 | dialog.title = "Test Save"
35 | # dialog.directory = ""
36 | dialog.defaultName = "default.txt"
37 | dialog.run()
38 | if dialog.file == "":
39 | textArea.addLine("No file selected")
40 | else:
41 | textArea.addLine(dialog.file)
42 |
43 | var button3 = newButton("Select Directory ...")
44 | buttons.add(button3)
45 | button3.onClick = proc(event: ClickEvent) =
46 | var dialog = SelectDirectoryDialog()
47 | dialog.title = "Test Select Directory"
48 | # dialog.startDirectory = ""
49 | dialog.run()
50 | if dialog.selectedDirectory == "":
51 | textArea.addLine("No directory selected")
52 | else:
53 | textArea.addLine(dialog.selectedDirectory)
54 |
55 | window.show()
56 | app.run()
57 |
--------------------------------------------------------------------------------
/examples/example_13_fixed_layout.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to have controls in a fixed position.
2 | # This means that NiGui's automatic layout capabilities are not used and the controls have to be placed manually.
3 |
4 | import nigui
5 |
6 | app.init()
7 |
8 | var window = newWindow()
9 |
10 | var container = newContainer()
11 | window.add(container)
12 |
13 | # Add a Button control:
14 | var button = newButton("Button at 0,0")
15 | container.add(button)
16 | button.x = 0
17 | button.y = 0
18 | button.width = 200
19 | button.height = 50
20 |
21 | # Add another button:
22 | button = newButton("Button at 300,0")
23 | container.add(button)
24 | button.x = 300
25 | button.y = 0
26 | button.width = 200
27 | button.height = 50
28 |
29 | # Add another button:
30 | button = newButton("Button at 0,100")
31 | container.add(button)
32 | button.x = 0
33 | button.y = 100
34 | button.width = 200
35 | button.height = 50
36 |
37 | window.show()
38 |
39 | app.run()
40 |
--------------------------------------------------------------------------------
/examples/example_14_container_scrolling.nim:
--------------------------------------------------------------------------------
1 | # This example shows an inner container with a scrollbar.
2 | # Result:
3 | # topContainer will take as many space as needed for the 5 labels.
4 | # scrollContainer takes the remaining space.
5 | # There is only one scrollbar:
6 | # The vertical scrollbar in scrollContainer, because it's height is insufficient for the 25 labels.
7 | # When the window height is increased, the scrollbar disappears.
8 |
9 | import NiGui
10 |
11 | app.init()
12 |
13 | var window = newWindow()
14 | window.width = 800
15 | window.height = 500
16 |
17 | var mainContainer = newLayoutContainer(Layout_Vertical)
18 | window.add(mainContainer)
19 |
20 | var topContainer = newLayoutContainer(Layout_Vertical)
21 | mainContainer.add(topContainer)
22 |
23 | for i in 1..5:
24 | topContainer.add(newLabel("Label in topContainer #" & $i))
25 |
26 | var scrollContainer = newLayoutContainer(Layout_Vertical)
27 | mainContainer.add(scrollContainer)
28 | scrollContainer.heightMode = HeightMode_Expand
29 | scrollContainer.widthMode = WidthMode_Expand
30 |
31 | for i in 1..25:
32 | scrollContainer.add(newLabel("Label in scrollContainer #" & $i))
33 |
34 | window.show()
35 | app.run()
36 |
--------------------------------------------------------------------------------
/examples/example_15_threading.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to change a control from another thread
2 |
3 | import nigui
4 |
5 | var
6 | window: Window
7 | container: LayoutContainer
8 | button: Button
9 | pbar: ProgressBar
10 | thread: Thread[void]
11 |
12 | app.init()
13 |
14 | window = newWindow("NiGui Example")
15 |
16 | container = newLayoutContainer(Layout_Vertical)
17 | container.padding = 10
18 | window.add(container)
19 |
20 | button = newButton("Start thread")
21 | container.add(button)
22 |
23 | proc update() =
24 | {.gcsafe.}:
25 | pbar.value = pbar.value + 0.01
26 | app.sleep(100)
27 | app.queueMain(update)
28 |
29 | proc start() =
30 | {.gcsafe.}:
31 | app.queueMain(update)
32 |
33 | button.onClick = proc(event: ClickEvent) =
34 | container.remove(button)
35 |
36 | pbar = newProgressBar()
37 | container.add(pbar)
38 |
39 | createThread(thread, start)
40 |
41 | window.show()
42 | app.run()
43 |
--------------------------------------------------------------------------------
/examples/example_30_custom_button.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to make a custom button (subclassing).
2 |
3 | import nigui
4 |
5 |
6 | # Definition of a custom button
7 | type CustomButton* = ref object of Button
8 |
9 | # Custom widgets need to draw themselves
10 | method handleDrawEvent(control: CustomButton, event: DrawEvent) =
11 | let canvas = event.control.canvas
12 | canvas.areaColor = rgb(55, 55, 55)
13 | canvas.textColor = rgb(255, 255, 255)
14 | canvas.lineColor = rgb(255, 255, 255)
15 | canvas.drawRectArea(0, 0, control.width, control.height)
16 | canvas.drawTextCentered(control.text)
17 | canvas.drawRectOutline(0, 0, control.width, control.height)
18 |
19 | # Override nigui.newButton (optional)
20 | proc newButton*(text = ""): Button =
21 | result = new CustomButton
22 | result.init()
23 | result.text = text
24 |
25 |
26 | # Main program
27 |
28 | app.init()
29 |
30 | var window = newWindow()
31 |
32 | var container = newLayoutContainer(Layout_Vertical)
33 | window.add(container)
34 |
35 | # Since newButton is overriden, this code is exactly the same as in a program using the native button
36 | var button = newButton("Button")
37 | container.add(button)
38 |
39 | var textArea = newTextArea()
40 | container.add(textArea)
41 |
42 | button.onClick = proc(event: ClickEvent) =
43 | textArea.addLine("Button clicked")
44 |
45 | window.show()
46 |
47 | app.run()
48 |
--------------------------------------------------------------------------------
/examples/example_31_custom_widget.nim:
--------------------------------------------------------------------------------
1 | # This example shows how to make a custom widget (subclassing).
2 |
3 | import nigui
4 |
5 |
6 | # Definition of a custom widget
7 | type MyWidget* = ref object of ControlImpl
8 | counter: int
9 |
10 | # Custom widgets need to draw themselves
11 | method handleDrawEvent(control: MyWidget, event: DrawEvent) =
12 | let canvas = event.control.canvas
13 | canvas.areaColor = rgb(55, 55, 55)
14 | canvas.textColor = rgb(255, 255, 255)
15 | canvas.lineColor = rgb(255, 255, 255)
16 | canvas.drawRectArea(0, 0, control.width, control.height)
17 | canvas.drawTextCentered($control.counter)
18 | canvas.drawRectOutline(0, 0, control.width, control.height)
19 |
20 | # Custom click handler
21 | method handleClickEvent(control: MyWidget, event: ClickEvent) =
22 | procCall control.ControlImpl.handleClickEvent(event)
23 | control.counter.inc
24 | control.forceRedraw
25 |
26 | # Constructor (optional)
27 | proc newMyWidget*(): MyWidget =
28 | result = new MyWidget
29 | result.init()
30 | result.width = 100.scaleToDpi
31 | result.height = 50.scaleToDpi
32 |
33 |
34 | # Main program
35 |
36 | app.init()
37 |
38 | var window = newWindow()
39 |
40 | var container = newLayoutContainer(Layout_Vertical)
41 | window.add(container)
42 |
43 | var myWidget = newMyWidget()
44 | container.add(myWidget)
45 |
46 | var textArea = newTextArea()
47 | container.add(textArea)
48 |
49 | myWidget.onClick = proc(event: ClickEvent) =
50 | textArea.addLine("myWidget clicked")
51 |
52 | window.show()
53 |
54 | app.run()
55 |
--------------------------------------------------------------------------------
/examples/example_90_widget_error.nim:
--------------------------------------------------------------------------------
1 | # This example shows what happens when a nigui widget error occurs.
2 |
3 | import nigui
4 |
5 | app.init()
6 |
7 | var window = newWindow()
8 |
9 | window.add(newButton("Button 1")) # ok
10 | window.add(newButton("Button 2")) # not ok, gives error messages, but program continues
11 |
12 | window.show()
13 | app.run()
14 |
--------------------------------------------------------------------------------
/examples/example_91_unhandled_exception.nim:
--------------------------------------------------------------------------------
1 | # This example shows what happens when an unhandled exception occurs in the main loop.
2 |
3 | import nigui
4 |
5 | app.init()
6 |
7 | var window = newWindow()
8 |
9 | var container = newLayoutContainer(Layout_Vertical)
10 | window.add(container)
11 |
12 | var button = newButton("Raise Exception")
13 | container.add(button)
14 | button.onClick = proc(event: ClickEvent) =
15 | raise newException(Exception, "Test Exception")
16 |
17 | window.show()
18 | app.run()
19 |
--------------------------------------------------------------------------------
/examples/nim.cfg:
--------------------------------------------------------------------------------
1 | --threads:on
2 |
--------------------------------------------------------------------------------
/faq.md:
--------------------------------------------------------------------------------
1 | NiGui FAQ
2 | =========
3 |
4 | ### Is there a GUI builder planned?
5 |
6 | No, NiGui is intended to define your user interface by manually written Nim source code.
7 | Using a GUI builder is maybe faster at the start of a project, but to modify an existing UI, it faster to change a few lines of code, instead of dragging around a bunch of widgets.
8 |
9 | ### How big is the generated executable?
10 |
11 | Sizes of "example_01_basic_app":
12 | Linux x64 binary: 608 kB
13 | Windows x64 binary: 705 kB
14 |
15 | ### Does NiGui support customization?
16 |
17 | NiGui allows you to use the native controls like buttons, labels, and text boxes. Typically the look of native controls can't be changed much. For example Windows does not support changing the background color of a button.
18 | As an alternative to native controls, NiGui allows you to create custom controls. Custom controls must draw their surface by it's own, therefore it can look like anything you want. As an example: To create a custom button, you can inherit from the type `Button` and override the method `handleDrawEvent()`.
19 | To make it possible to adjust the look of custom controls to the look of native controls, NiGui allows you to use the platform's default styles, for example `app.defaultBackgroundColor` and `app.defaultTextColor`.
20 |
21 | ### How does NiGui compare to other GUI toolkits?
22 |
23 | *Please add your knowlege here if you know more.*
24 |
25 | #### NiGui compared to Gtk
26 |
27 | Gtk is a popular cross-platform GUI toolkit written in C.
28 | Gtk uses native widgets partly.
29 | Unlike to NiGui, under Windows the user has to install Gtk or you ship the DLL files with your application.
30 | The Gtk 2 DLL files for Windows takes about 30 MB.
31 | Under the desktop Linux distributions, Gtk 2 and 3 are usually preinstalled.
32 | NiGui uses Gtk 3 as backend under Linux.
33 |
34 | #### NiGui compared to Qt
35 |
36 | Qt is a popular cross-platform GUI toolkit written in C++.
37 | It uses a preprocessor for the C++ code, therefore it cannot be used with other programming languages like C or Nim.
38 |
39 | #### NiGui compared to wxWidgets
40 |
41 | wxWidgets is a cross-platform GUI toolkit written in C++.
42 | Unlike NiGui, under Windows the user has to install wxWidgets, or you have to ship the DLL files with your application, or you can statically link wxWidgets into your application.
43 | The wxWidgets DLL files for Windows takes about 20 MB (about the same overhead applies to static linking).
44 | wxWidgets can be used in Nim (https://github.com/Araq/wxnim/).
45 |
46 | #### NiGui compared to IUP
47 |
48 | IUP is a cross-platform GUI toolkit written in C (http://webserver2.tecgraf.puc-rio.br/iup/).
49 | Like NiGui, IUP uses Gtk and Win32 as backends.
50 | IUP can be used in Nim (https://github.com/nim-lang/iup).
51 | Using a C library in Nim is better than most languages but not perfect. For example the user has to install the library according to the instructions from the wrapper. With a pure Nim project you only need to install the package.
52 |
53 | #### NiGui compared to libui
54 |
55 | libui is a cross-platform GUI toolkit written in C (https://github.com/andlabs/libui).
56 | Like NiGui, libui uses Gtk and Win32 as backends and uses native controls.
57 | Like NiGui, libui is in the early development phase (at least no documentation is written).
58 | libui can be used in Nim (https://github.com/nim-lang/ui).
59 | Using a C library in Nim is better than most languages but not perfect. For example the user has to install the library according to the instructions from the wrapper. With a pure Nim project you only need to install the package.
60 |
61 | #### NiGui compared to nimx
62 |
63 | nimx is a cross-platform GUI toolkit written in Nim (https://github.com/yglukhov/nimx).
64 | It uses OpenGL as backend and also works in a web browser.
65 | There are no native controls.
66 | Like NiGui, nimx is in the early development phase (at least no documentation is written).
67 |
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | Copyright 2017 Simon Krauter
2 |
3 | [MIT License](https://opensource.org/licenses/MIT)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/nigui.nimble:
--------------------------------------------------------------------------------
1 | # Package
2 |
3 | version = "0.2.8"
4 | author = "Simon Krauter"
5 | description = "Cross-platform, desktop GUI toolkit using native widgets."
6 | license = "MIT"
7 |
8 | # Deps
9 | requires "nim >= 1.0.0"
10 |
11 | srcDir = "src"
12 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | NiGui
2 | =====
3 |
4 | NiGui is a cross-platform desktop GUI toolkit written in [Nim](https://nim-lang.org/).
5 | NiGui provides an easy way to develop applications in Nim with a full-featured graphical user interface.
6 |
7 | Target platforms:
8 | * Windows (Win32 API)
9 | * Linux over GTK+ 3
10 | * macOS over GTK+ 3 (native support planned)
11 |
12 | Design goals:
13 | * **Full abstraction**
14 | NiGui provides full abstraction of the underlying platform. NiGui applications are written once and can be compiled for different platforms. Application developers don't have to care about platform-specific details.
15 | * **Simple and elegant**
16 | NiGui has a clean and beginner-friendly high-level API. It is much less complex than the Win32 API, GTK+ or Qt.
17 | NiGui profits of Nim's features and elegance in contrast to C code, for example Nim's polymorphism capabilities.
18 | * **Powerful**
19 | NiGui uses the native controls of the underlying platform to give a familiar use and feel for the user. In addtion, NiGui allows to create custom controls for special use cases or a themed UI.
20 | NiGui has it's own layout manager for automatic resizing and positioning of controls.
21 | * **Minimal dependencies**
22 | The NiGui source code has no dependencies except Nim's standard library. Platform bindings are included.
23 | Generated binaries (exe files) include NiGui and do not need external libraries.
24 |
25 | Screenshots
26 | -------------
27 |
28 | Example program with native controls running under Windows 10 and Xubuntu:
29 |
30 |
31 | Current state
32 | -------------
33 | NiGui is currently work in progress. Very basic things work, many things are missing.
34 |
35 | Working:
36 | * Window, Button, Label, TextBox, TextArea
37 | * LayoutContainer (own layout manager)
38 | * Timers
39 | * Message boxes and file dialogs
40 | * Custom controls including scrolling
41 | * Drawing and image processing
42 |
43 | WIP:
44 | * Event handling
45 | * Documentation
46 |
47 | Planned:
48 | * macOS support
49 | * More widgets
50 |
51 | Getting started
52 | ---------------
53 |
54 | ### How to install NiGui manually
55 |
56 | 1. Clone the NiGui repository with Git or download the source code
57 | 2. Add the following line to one of your [Nim configuration files](https://nim-lang.org/docs/nimc.html#compiler-usage-configuration-files):
58 | `--path:"/src"`
59 |
60 | ### How to install NiGui with Nimble
61 |
62 | Run the [Nimble](https://github.com/nim-lang/nimble) install command: `$ nimble install nigui`
63 |
64 | ### Additional configuration
65 |
66 | * To disable the command line window under Windows, add this line to your Nim configuration: `--app:gui`
67 | * To compile a Windows binary which uses Gtk, add this line to your Nim configuration: `-d:forceGtk`
68 |
69 | ### How to verify the installation
70 |
71 | Compile and run one of the included example programs.
72 |
73 | Show cases
74 | ----------
75 | * [NiCalc](https://github.com/trustable-code/NiCalc) - Simple calculator
76 | * [Gravitate](https://www.qtrac.eu/gravitate.html) - Samegame/tilefall-like game
77 |
78 | Contributing
79 | ------------
80 | You can help to improve NiGui by:
81 | * Trying to use it and giving feedback
82 | * Test the programs under different Windows versions or Linux distributions
83 | * Developing show cases
84 | * Help improving and extending the code
85 | * Adding macOS support
86 |
87 | Contact: see https://github.com/trustable-code
88 |
89 | FAQ
90 | ---
91 |
92 | [FAQ](faq.md)
93 |
94 | License
95 | -------
96 | NiGui is FLOSS (free and open-source software).
97 | All files in this repository are licensed under the [MIT License](https://opensource.org/licenses/MIT). As a result you may use any compatible license (essentially any license) for your own programs developed with NiGui. You are explicitly permitted to develop commercial applications using NiGui.
98 | Copyright 2017-2020 Simon Krauter and contributors
99 |
--------------------------------------------------------------------------------
/screenshots.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simonkrauter/NiGui/f8167a82517973d7cd47797801f90b06360e3f32/screenshots.png
--------------------------------------------------------------------------------
/src/nigui/msgbox.nim:
--------------------------------------------------------------------------------
1 | # NiGui - extended message box
2 |
3 | # This module provides an extended message box.
4 | # The message box is shown as modal window.
5 | # The message box can have up to 3 buttons with customizable titles.
6 | # button1 will be focused.
7 | # Call the proc msgBox() to open the message box.
8 | # It will wait until the message box is closed.
9 | # Meaning of the result value:
10 | # 0 - message box was closed over the window close button or escape key
11 | # 1..3 - button 1..3 clicked
12 |
13 | # For an example see "example_04_msgboxes.nim".
14 |
15 | import nigui
16 |
17 | type MessageBoxWindow = ref object of WindowImpl
18 | clickedButton: Button
19 |
20 | proc buttonClick(event: ClickEvent) =
21 | cast[MessageBoxWindow](event.control.parentWindow).clickedButton = cast[Button](event.control)
22 | event.control.parentWindow.dispose()
23 |
24 | proc msgBox*(parent: Window, message: string, title = "Message", button1 = "OK", button2, button3: string = ""): int {.discardable.} =
25 | let buttonMinWidth = 100.scaleToDpi
26 | var window = new MessageBoxWindow
27 | window.init()
28 | window.title = title
29 |
30 | window.onKeyDown = proc(event: KeyboardEvent) =
31 | if event.key == Key_Escape:
32 | window.dispose()
33 |
34 | var container = newLayoutContainer(Layout_Vertical)
35 | container.padding = 10.scaleToDpi
36 | window.control = container
37 |
38 | var labelContainer = newLayoutContainer(Layout_Horizontal)
39 | container.add(labelContainer)
40 | labelContainer.widthMode = WidthMode_Expand
41 | labelContainer.heightMode = HeightMode_Expand
42 |
43 | var label = newLabel(message)
44 | labelContainer.add(label)
45 |
46 | var buttonContainer = newLayoutContainer(Layout_Horizontal)
47 | buttonContainer.widthMode = WidthMode_Expand
48 | buttonContainer.xAlign = XAlign_Center
49 | buttonContainer.spacing = 12.scaleToDpi
50 | container.add(buttonContainer)
51 | var b1, b2, b3: Button
52 |
53 | b1 = newButton(button1)
54 | b1.minWidth = buttonMinWidth
55 | b1.onClick = buttonClick
56 | buttonContainer.add(b1)
57 | b1.focus()
58 |
59 | if button2 != "":
60 | b2 = newButton(button2)
61 | b2.minWidth = buttonMinWidth
62 | b2.onClick = buttonClick
63 | buttonContainer.add(b2)
64 |
65 | if button3 != "":
66 | b3 = newButton(button3)
67 | b3.minWidth = buttonMinWidth
68 | b3.onClick = buttonClick
69 | buttonContainer.add(b3)
70 |
71 | window.width = min(max(label.width + 40.scaleToDpi, buttonMinWidth * 3 + 65.scaleToDpi), 600.scaleToDpi)
72 | window.height = min(label.height, 300.scaleToDpi) + buttonContainer.height + 70.scaleToDpi
73 |
74 | # Center message box on window:
75 | window.x = parent.x + ((parent.width - window.width) div 2)
76 | window.y = parent.y + ((parent.height - window.height) div 2)
77 |
78 | window.showModal(parent)
79 |
80 | while not window.disposed:
81 | app.sleep(100)
82 |
83 | if window.clickedButton == b1:
84 | result = 1
85 | elif window.clickedButton == b2 and b2 != nil:
86 | result = 2
87 | elif window.clickedButton == b3 and b3 != nil:
88 | result = 3
89 |
--------------------------------------------------------------------------------
/src/nigui/private/gtk3/gtk3.nim:
--------------------------------------------------------------------------------
1 | # NiGui - minimal GTK+ 3 binding
2 |
3 | when defined(windows):
4 | const libgtk3Path* = "libgtk-3-0.dll"
5 | elif defined(macosx):
6 | const libgtk3Path* = "libgtk-3.0.dylib"
7 | else:
8 | const libgtk3Path* = "libgtk-3.so(|.0)"
9 |
10 | {.pragma: libgtk3, cdecl, dynlib: libgtk3Path.}
11 |
12 | # ----------------------------------------------------------------------------------------
13 | # Types
14 | # ----------------------------------------------------------------------------------------
15 |
16 | type
17 | Gboolean* = distinct cint
18 |
19 | GError* {.bycopy.} = object
20 | domain*: int32
21 | code*: cint
22 | message*: cstring
23 |
24 | GdkRectangle* {.bycopy.} = object
25 | x*, y*: cint
26 | width*, height*: cint
27 |
28 | GtkBorder* {.bycopy.} = object
29 | left*: int16
30 | right*: int16
31 | top*: int16
32 | bottom*: int16
33 |
34 | GdkRGBA* {.bycopy.} = object
35 | red*: cdouble
36 | green*: cdouble
37 | blue*: cdouble
38 | alpha*: cdouble
39 |
40 | GList* = ptr object
41 | data*: pointer
42 | next*: GList
43 | prev*: GList
44 |
45 | GtkTargetEntry* {.bycopy.} = object
46 | target*: cstring
47 | flags*: cint
48 | info*: cint
49 |
50 | GdkEventButton* {.bycopy.} = object
51 | event_type*: cint
52 | window*: pointer
53 | send_event*: int8
54 | time*: cint
55 | x*, y*: cdouble
56 | axes*: ptr cdouble
57 | state*: cint
58 | button*: cint
59 | device*: pointer
60 | x_root*, y_root*: cdouble
61 |
62 | GdkEventMotion* {.bycopy.} = object
63 | event_type*: cint
64 | window*: pointer
65 | send_event*: int8
66 | time*: cint
67 | x*, y*: cdouble
68 | axes*: ptr cdouble
69 | state*: cint
70 | is_hint*: int16
71 | device*: pointer
72 | x_root*, y_root*: cdouble
73 |
74 | GdkEventCrossing* {.bycopy.} = object
75 | event_type*: cint
76 | window*: pointer
77 | send_event*: int8
78 | subwindow*: pointer
79 | time*: cint
80 | x*, y*: cdouble
81 | x_root*, y_root*: cdouble
82 | mode*: cint
83 | detail*: cint
84 | focus*: bool
85 | device*: pointer
86 | state*: cint
87 |
88 | GdkEventKey* {.bycopy.} = object
89 | event_type*: cint
90 | window*: pointer
91 | send_event*: int8
92 | time*: cint
93 | state*: cint
94 | keyval*: cint
95 | length*: cint
96 | `string`*: cstring
97 | hardware_keycode*: int16
98 | group*: int8
99 | is_modifier*: int8
100 |
101 | GdkEventWindowState* {.bycopy.} = object
102 | event_type*: cint
103 | window*: pointer
104 | send_event*: int8
105 | changed_mask*: cint
106 | new_window_state*: cint
107 |
108 | GdkEventFocus* {.bycopy.} = object
109 | event_type*: cint
110 | window*: pointer
111 | send_event*: int8
112 | `in`*: int16
113 |
114 | GtkTextIter* {.bycopy.} = object
115 | dummy1: pointer
116 | dummy2: pointer
117 | dummy3: cint
118 | dummy4: cint
119 | dummy5: cint
120 | dummy6: cint
121 | dummy7: cint
122 | dummy8: cint
123 | dummy9: pointer
124 | dummy10: pointer
125 | dummy11: cint
126 | dummy12: cint
127 | dummy13: cint
128 | dummy14: pointer
129 |
130 | GdkGeometry* {.bycopy.} = object
131 | min_width*: cint
132 | min_height*: cint
133 | max_width*: cint
134 | max_height*: cint
135 | base_width*: cint
136 | base_height*: cint
137 | width_inc*: cint
138 | height_inc*: cint
139 | min_aspect*: cdouble
140 | max_aspect*: cdouble
141 | win_gravity*: cint
142 |
143 |
144 | converter gbool*(val: bool): Gboolean = ord(val).Gboolean
145 |
146 | converter toBool*(val: Gboolean): bool = int(val) != 0
147 |
148 |
149 | # ----------------------------------------------------------------------------------------
150 | # Constants
151 | # ----------------------------------------------------------------------------------------
152 |
153 | const
154 | # GtkWindowType:
155 | GTK_WINDOW_TOPLEVEL* = 0
156 | GTK_WINDOW_POPUP* = 1
157 |
158 | # GtkDestDefaults:
159 | # [..]
160 | GTK_DEST_DEFAULT_ALL* = 7
161 |
162 | # GdkDragAction:
163 | GDK_ACTION_DEFAULT* = 1
164 | GDK_ACTION_COPY* = 2
165 | GDK_ACTION_MOVE* = 4
166 | GDK_ACTION_LINK* = 8
167 | GDK_ACTION_PRIVATE* = 16
168 | GDK_ACTION_ASK* = 32
169 |
170 | # GtkOrientation:
171 | GTK_ORIENTATION_HORIZONTAL* = 0
172 | GTK_ORIENTATION_VERTICAL* = 1
173 |
174 | # GtkWrapMode:
175 | GTK_WRAP_NONE* = 0
176 | GTK_WRAP_CHAR* = 1
177 | GTK_WRAP_WORD* = 2
178 | GTK_WRAP_WORD_CHAR* = 3
179 |
180 | # GtkPolicyType:
181 | GTK_POLICY_ALWAYS* = 0
182 | GTK_POLICY_AUTOMATIC* = 1
183 | GTK_POLICY_NEVER* = 2
184 | GTK_POLICY_EXTERNAL* = 3
185 |
186 | # PangoEllipsizeMode:
187 | PANGO_ELLIPSIZE_NONE* = 0
188 | PANGO_ELLIPSIZE_START* = 1
189 | PANGO_ELLIPSIZE_MIDDLE* = 2
190 | PANGO_ELLIPSIZE_END* = 3
191 |
192 | # GtkButtonBoxStyle:
193 | GTK_BUTTONBOX_SPREAD* = 0
194 | GTK_BUTTONBOX_EDGE* = 1
195 | GTK_BUTTONBOX_START* = 2
196 | GTK_BUTTONBOX_END* = 3
197 | GTK_BUTTONBOX_CENTER* = 4
198 | GTK_BUTTONBOX_EXPAND* = 5
199 |
200 | # GtkJustification:
201 | GTK_JUSTIFY_LEFT* = 0
202 | GTK_JUSTIFY_RIGHT* = 1
203 | GTK_JUSTIFY_CENTER* = 2
204 | GTK_JUSTIFY_FILL* = 3
205 |
206 | # GtkStateFlags:
207 | GTK_STATE_FLAG_NORMAL* = 0
208 | # [..]
209 |
210 | # GdkEventMask:
211 | GDK_POINTER_MOTION_MASK* = 1 shl 2
212 | GDK_BUTTON_PRESS_MASK* = 1 shl 8
213 | GDK_BUTTON_RELEASE_MASK* = 1 shl 9
214 | GDK_KEY_PRESS_MASK* = 1 shl 10
215 | GDK_ENTER_NOTIFY_MASK* = 1 shl 12
216 | GDK_LEAVE_NOTIFY_MASK* = 1 shl 13
217 | # [..]
218 |
219 | # cairo_format_t:
220 | CAIRO_FORMAT_ARGB32* = 0
221 | # [..]
222 |
223 | # cairo_filter_t:
224 | CAIRO_FILTER_NEAREST* = 3
225 | CAIRO_FILTER_BILINEAR* = 4
226 |
227 | # GtkFileChooserAction:
228 | GTK_FILE_CHOOSER_ACTION_OPEN* = 0
229 | GTK_FILE_CHOOSER_ACTION_SAVE* = 1
230 | GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER* = 2
231 | GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER* = 3
232 |
233 | # GtkResponseType:
234 | GTK_RESPONSE_NONE* = -1
235 | GTK_RESPONSE_REJECT* = -2
236 | GTK_RESPONSE_ACCEPT* = -3
237 | GTK_RESPONSE_DELETE_EVENT* = -4
238 | GTK_RESPONSE_OK* = -5
239 | GTK_RESPONSE_CANCEL* = -6
240 | GTK_RESPONSE_CLOSE* = -7
241 | GTK_RESPONSE_YES* = -8
242 | GTK_RESPONSE_NO* = -9
243 | GTK_RESPONSE_APPLY* = -10
244 | GTK_RESPONSE_HELP* = -11
245 |
246 | # Selection:
247 | GDK_SELECTION_CLIPBOARD* = cast[pointer](69)
248 |
249 | # Key modifier masks:
250 | GDK_CONTROL_MASK* = 1 shl 2
251 |
252 | # GdkWindowState:
253 | GDK_WINDOW_STATE_WITHDRAWN* = 1
254 | GDK_WINDOW_STATE_ICONIFIED* = 2
255 | GDK_WINDOW_STATE_MAXIMIZED* = 4
256 | GDK_WINDOW_STATE_STICKY* = 8
257 | GDK_WINDOW_STATE_FULLSCREEN* = 16
258 | GDK_WINDOW_STATE_ABOVE* = 32
259 | GDK_WINDOW_STATE_BELOW* = 64
260 | GDK_WINDOW_STATE_FOCUSED* = 128
261 | GDK_WINDOW_STATE_TILED* = 256
262 |
263 | # GSignalMatchType:
264 | G_SIGNAL_MATCH_ID* = 1
265 | G_SIGNAL_MATCH_DETAIL* = 2
266 | G_SIGNAL_MATCH_CLOSURE* = 4
267 | G_SIGNAL_MATCH_FUNC* = 8
268 | G_SIGNAL_MATCH_DATA* = 16
269 | G_SIGNAL_MATCH_UNBLOCKED* = 16
270 |
271 | # GdkWindowTypeHint
272 | GDK_WINDOW_TYPE_HINT_NORMAL* = 0
273 | GDK_WINDOW_TYPE_HINT_DIALOG* = 1
274 | GDK_WINDOW_TYPE_HINT_MENU* = 2
275 | GDK_WINDOW_TYPE_HINT_TOOLBAR* = 3
276 | GDK_WINDOW_TYPE_HINT_SPLASHSCREEN* = 4
277 | GDK_WINDOW_TYPE_HINT_UTILITY* = 5
278 | GDK_WINDOW_TYPE_HINT_DOCK* = 6
279 | GDK_WINDOW_TYPE_HINT_DESKTOP* = 7
280 | GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU* = 8
281 | GDK_WINDOW_TYPE_HINT_POPUP_MENU* = 9
282 | GDK_WINDOW_TYPE_HINT_TOOLTIP* = 10
283 | GDK_WINDOW_TYPE_HINT_NOTIFICATION* = 11
284 | GDK_WINDOW_TYPE_HINT_COMBO* = 12
285 | GDK_WINDOW_TYPE_HINT_DND* = 13
286 |
287 | # GdkWindowHints
288 | GDK_HINT_MIN_SIZE* = 2
289 |
290 |
291 | # ----------------------------------------------------------------------------------------
292 | # General Gtk Procs
293 | # ----------------------------------------------------------------------------------------
294 |
295 | proc g_slist_length*(list: pointer): int {.importc, libgtk3.}
296 | proc g_slist_nth_data*(list: pointer, n: int): cstring {.importc, libgtk3.}
297 | proc g_object_ref*(`object`: pointer): pointer {.importc, libgtk3.}
298 | proc g_object_unref*(`object`: pointer) {.importc, libgtk3.}
299 |
300 | proc gtk_init*(argc, argv: pointer) {.importc, libgtk3.}
301 |
302 | proc gtk_main*() {.importc, libgtk3.}
303 | proc gtk_main_quit*() {.importc, libgtk3.}
304 | proc gtk_events_pending*(): cint {.importc, libgtk3.}
305 | proc gtk_main_iteration*(): cint {.importc, libgtk3.}
306 | proc g_timeout_add*(interval: cint, function, data: pointer): cint {.importc, libgtk3.}
307 | proc g_source_remove*(tag: cint): bool {.importc, libgtk3.}
308 | proc g_signal_connect_data*(instance: pointer, detailed_signal: cstring, c_handler: pointer, data, destroy_data, connect_flags: pointer = nil): pointer {.importc, libgtk3.}
309 | proc g_signal_handlers_block_matched*(instance: pointer, mask, signal_id: cint, detail, closure, function, data: pointer = nil) {.importc, libgtk3.}
310 | proc g_signal_handlers_unblock_matched*(instance: pointer, mask, signal_id: cint, detail, closure, function, data: pointer = nil) {.importc, libgtk3.}
311 |
312 | proc gtk_window_new*(`type`: cint): pointer {.importc, libgtk3.}
313 | proc gtk_window_set_title*(window: pointer, title: cstring) {.importc, libgtk3.}
314 | # proc gtk_window_get_title*(window: pointer): cstring {.importc, libgtk3.}
315 | proc gtk_window_set_transient_for*(window, parent: pointer) {.importc, libgtk3.}
316 | proc gtk_window_set_modal*(window: pointer, modal: cint) {.importc, libgtk3.}
317 | proc gtk_window_set_type_hint*(window: pointer, hint: cint) {.importc, libgtk3.}
318 | proc gtk_window_set_default_size*(window: pointer, width, height: cint) {.importc, libgtk3.}
319 | proc gtk_window_resize*(window: pointer, width, height: cint) {.importc, libgtk3.}
320 | proc gtk_window_resize_to_geometry*(window: pointer, width, height: cint) {.importc, libgtk3.}
321 | proc gtk_window_set_resizable*(window: pointer, resizable: bool) {.importc, libgtk3.}
322 | proc gtk_window_set_geometry_hints*(window: pointer, geometry_widget: pointer, geometry: var GdkGeometry, geom_mask: cint) {.importc, libgtk3.}
323 | proc gtk_window_get_size*(window: pointer, width, height: var cint) {.importc, libgtk3.}
324 | proc gtk_window_get_position*(window: pointer, x, y: var cint) {.importc, libgtk3.}
325 | proc gtk_window_move*(window: pointer, x, y: cint) {.importc, libgtk3.}
326 | proc gtk_window_set_icon_from_file*(window: pointer, filename: cstring, err: pointer): bool {.importc, libgtk3.}
327 | proc gtk_window_iconify*(window: pointer) {.importc, libgtk3.}
328 | proc gtk_window_deiconify*(window: pointer) {.importc, libgtk3.}
329 | proc gtk_window_present*(window: pointer) {.importc, libgtk3.}
330 | proc gtk_window_set_keep_above*(window: pointer, setting: bool) {.importc, libgtk3.}
331 |
332 | proc gdk_window_begin_paint_rect*(window: pointer, rectangle: var GdkRectangle) {.importc, libgtk3.}
333 | proc gdk_window_begin_paint_region*(window: pointer, region: pointer) {.importc, libgtk3.}
334 | proc gdk_window_end_paint*(window: pointer) {.importc, libgtk3.}
335 | proc gdk_window_get_clip_region*(window: pointer): pointer {.importc, libgtk3.}
336 |
337 | proc gtk_widget_destroy*(widget: pointer) {.importc, libgtk3.}
338 | proc gtk_widget_show*(widget: pointer) {.importc, libgtk3.}
339 | proc gtk_widget_hide*(widget: pointer) {.importc, libgtk3.}
340 | proc gtk_widget_set_size_request*(widget: pointer, width, height: cint) {.importc, libgtk3.}
341 | proc gtk_widget_size_allocate*(widget: pointer, allocation: var GdkRectangle) {.importc, libgtk3.}
342 | proc gtk_widget_get_size_request*(widget: pointer, width, height: var cint) {.importc, libgtk3.}
343 | proc gtk_widget_get_allocation*(widget: pointer, allocation: var GdkRectangle) {.importc, libgtk3.}
344 | # proc gtk_widget_set_allocation*(widget: pointer, allocation: var GdkRectangle) {.importc, libgtk3.}
345 | # proc gtk_widget_set_hexpand*(widget: pointer, expand: cint) {.importc, libgtk3.}
346 | proc gtk_widget_queue_draw*(widget: pointer) {.importc, libgtk3.}
347 | proc gtk_widget_set_margin_top*(widget: pointer, margin: cint) {.importc, libgtk3.}
348 | proc gtk_widget_add_events*(widget: pointer, events: cint) {.importc, libgtk3.}
349 | proc gtk_widget_set_can_focus*(widget: pointer, can_focus: cint) {.importc, libgtk3.}
350 | proc gtk_widget_modify_font*(widget: pointer, font_desc: pointer) {.importc, libgtk3.}
351 | proc gtk_widget_override_color*(widget: pointer, state: cint, color: var GdkRGBA) {.importc, libgtk3.}
352 | proc gtk_widget_override_background_color*(widget: pointer, state: cint, color: var GdkRGBA) {.importc, libgtk3.}
353 | proc gtk_widget_get_path*(widget: pointer): pointer {.importc, libgtk3.}
354 | proc gtk_widget_style_get*(widget: pointer, first_property_name: cstring, value: pointer, passNil: pointer) {.importc, libgtk3.}
355 | proc gtk_widget_get_style_context*(widget: pointer): pointer {.importc, libgtk3.}
356 | proc gtk_widget_grab_focus*(widget: pointer) {.importc, libgtk3.}
357 | proc gtk_widget_is_focus*(widget: pointer): bool {.importc, libgtk3.}
358 | proc gtk_widget_realize*(widget: pointer) {.importc, libgtk3.}
359 | proc gtk_widget_draw*(widget, cr: pointer) {.importc, libgtk3.}
360 | proc gtk_widget_set_sensitive*(widget: pointer, sensitive: bool) {.importc, libgtk3.}
361 | proc gtk_widget_get_pointer*(widget: pointer, x, y: var cint) {.importc, libgtk3.}
362 |
363 | proc gtk_container_add*(container, widget: pointer) {.importc, libgtk3.}
364 | proc gtk_container_remove*(container, widget: pointer) {.importc, libgtk3.}
365 | # proc gtk_container_foreach*(container, callback, callback_data: pointer) {.importc, libgtk3.}
366 | proc gtk_container_get_children*(container: pointer): GList {.importc, libgtk3.}
367 | proc gtk_container_set_border_width*(container: pointer, width: cint) {.importc, libgtk3.}
368 |
369 | proc gtk_fixed_new*(): pointer {.importc, libgtk3.}
370 | proc gtk_fixed_move*(fixed, widget: pointer, x, y: cint) {.importc, libgtk3.}
371 |
372 | proc gtk_layout_new*(hadjustment, vadjustment: pointer): pointer {.importc, libgtk3.}
373 | # proc gtk_layout_put*(layout, child_widget: pointer, x, y: cint) {.importc, libgtk3.}
374 | proc gtk_layout_move*(layout, child_widget: pointer, x, y: cint) {.importc, libgtk3.}
375 | proc gtk_layout_set_size*(layout: pointer, width, height: cint) {.importc, libgtk3.}
376 | # proc gtk_layout_get_hadjustment*(layout: pointer): pointer {.importc, libgtk3.}
377 | # proc gtk_layout_get_vadjustment*(layout: pointer): pointer {.importc, libgtk3.}
378 |
379 | # proc gtk_scrollbar_new*(orientation: GtkOrientation, adjustment: pointer): pointer {.importc, libgtk3.}
380 |
381 | proc gtk_dialog_new*(): pointer {.importc, libgtk3.}
382 | proc gtk_dialog_run*(dialog: pointer): cint {.importc, libgtk3.}
383 | proc gtk_dialog_add_button*(dialog: pointer, button_text: cstring, response_id: cint): pointer {.importc, libgtk3.}
384 | proc gtk_dialog_get_content_area*(dialog: pointer): pointer {.importc, libgtk3.}
385 | proc gtk_dialog_get_action_area*(dialog: pointer): pointer {.importc, libgtk3.}
386 |
387 | proc gtk_file_chooser_dialog_new*(title: cstring, parent: pointer, action: int, text1: cstring, response1: int, text2: cstring, response2: int, ending: pointer): pointer {.importc, libgtk3.}
388 | proc gtk_file_chooser_set_current_name*(chooser: pointer, name: cstring): bool {.importc, libgtk3.}
389 | proc gtk_file_chooser_get_filename*(chooser: pointer): cstring {.importc, libgtk3.}
390 | proc gtk_file_chooser_get_filenames*(chooser: pointer): pointer {.importc, libgtk3.}
391 | proc gtk_file_chooser_set_select_multiple*(chooser: pointer, select_multiple: bool) {.importc, libgtk3.}
392 | proc gtk_file_chooser_set_current_folder*(chooser: pointer, filename: cstring): bool {.importc, libgtk3.}
393 |
394 | proc gtk_button_box_set_layout*(widget: pointer, layout_style: cint) {.importc, libgtk3.}
395 |
396 | # proc gtk_message_dialog_new*(parent: pointer, flags: GtkDialogFlags, `type`: GtkMessageType, buttons: GtkButtonsType, message_format: cstring): pointer {.importc, libgtk3.}
397 |
398 | proc gtk_label_new*(str: cstring): pointer {.importc, libgtk3.}
399 | # proc gtk_label_set_text*(label: pointer, str: cstring) {.importc, libgtk3.}
400 | # proc gtk_label_get_text*(label: pointer): cstring {.importc, libgtk3.}
401 | proc gtk_label_set_ellipsize*(label: pointer, mode: cint) {.importc, libgtk3.}
402 | # proc gtk_label_set_justify*(label: pointer, jtype: cint) {.importc, libgtk3.}
403 | # proc gtk_label_set_xalign*(label: pointer, xalign: cfloat) {.importc, libgtk3.}
404 | # proc gtk_label_set_yalign*(label: pointer, yalign: cfloat) {.importc, libgtk3.}
405 |
406 | proc gtk_progress_bar_new*(): pointer {.importc, libgtk3.}
407 | proc gtk_progress_bar_set_fraction*(pbar: pointer, fraction: cdouble) {.importc, libgtk3.}
408 |
409 | proc gtk_level_bar_new*(): pointer {.importc, libgtk3.}
410 | proc gtk_level_bar_set_value*(pbar: pointer, value: cdouble) {.importc, libgtk3.}
411 |
412 | # proc gtk_box_new*(orientation: GtkOrientation, spacing: cint): pointer {.importc, libgtk3.}
413 | proc gtk_box_pack_start*(box, child: pointer, expand, fill: cint, padding: cint) {.importc, libgtk3.}
414 |
415 | proc gtk_button_new*(): pointer {.importc, libgtk3.}
416 | # proc gtk_button_new_with_label*(label: cstring): pointer {.importc, libgtk3.}
417 | # proc gtk_button_get_label*(button: pointer): cstring {.importc, libgtk3.}
418 | proc gtk_button_set_label*(button: pointer, label: cstring) {.importc, libgtk3.}
419 |
420 | proc gtk_check_button_new*(): pointer {.importc, libgtk3.}
421 | proc gtk_toggle_button_set_active*(toggle_button: pointer, is_active: bool) {.importc, libgtk3.}
422 | proc gtk_toggle_button_get_active*(toggle_button: pointer): bool {.importc, libgtk3.}
423 |
424 | proc gtk_combo_box_text_new*(): pointer {.importc, libgtk3.}
425 | proc gtk_combo_box_get_active*(combo_box: pointer): cint {.importc, libgtk3.}
426 | proc gtk_combo_box_set_active*(combo_box: pointer, index: cint) {.importc, libgtk3.}
427 | proc gtk_combo_box_text_get_active_text*(combo_box: pointer): cstring {.importc, libgtk3.}
428 | proc gtk_combo_box_text_append_text*(combo_box: pointer, text: cstring) {.importc, libgtk3.}
429 | proc gtk_combo_box_text_remove_all*(combo_box: pointer) {.importc, libgtk3.}
430 |
431 | proc gtk_entry_new*(): pointer {.importc, libgtk3.}
432 | proc gtk_entry_set_text*(entry: pointer, text: cstring) {.importc, libgtk3.}
433 | proc gtk_entry_get_text*(entry: pointer): cstring {.importc, libgtk3.}
434 | proc gtk_entry_set_width_chars*(entry: pointer, n_chars: cint) {.importc, libgtk3.}
435 | proc gtk_entry_set_placeholder_text*(entry: pointer, text: cstring) {.importc, libgtk3.}
436 | proc gtk_editable_get_selection_bounds*(editable: pointer, start_pos, end_pos: var cint): bool {.importc, libgtk3.}
437 | proc gtk_editable_get_chars*(editable: pointer, start_pos, end_pos: cint): cstring {.importc, libgtk3.}
438 | proc gtk_editable_select_region*(editable: pointer, start_pos, end_pos: cint) {.importc, libgtk3.}
439 | proc gtk_editable_get_position*(editable: pointer): cint {.importc, libgtk3.}
440 | proc gtk_editable_set_position*(editable: pointer, position: cint) {.importc, libgtk3.}
441 | proc gtk_editable_set_editable*(editable: pointer, is_editable: bool) {.importc, libgtk3.}
442 |
443 | proc gtk_text_view_new*(): pointer {.importc, libgtk3.}
444 | proc gtk_text_view_set_buffer*(text_view, buffer: pointer) {.importc, libgtk3.}
445 | proc gtk_text_view_get_buffer*(text_view: pointer): pointer {.importc, libgtk3.}
446 | proc gtk_text_view_set_wrap_mode*(text_view: pointer, wrap_mode: cint) {.importc, libgtk3.}
447 | proc gtk_text_view_set_left_margin*(text_view: pointer, margin: cint) {.importc, libgtk3.}
448 | proc gtk_text_view_set_right_margin*(text_view: pointer, margin: cint) {.importc, libgtk3.}
449 | proc gtk_text_view_set_top_margin*(text_view: pointer, margin: cint) {.importc, libgtk3.}
450 | proc gtk_text_view_set_bottom_margin*(text_view: pointer, margin: cint) {.importc, libgtk3.}
451 | proc gtk_text_view_scroll_to_iter*(text_view: pointer, iter: var GtkTextIter, within_margin: cdouble, use_align: bool, xalign, yalign: cdouble) {.importc, libgtk3.}
452 | # proc gtk_text_view_scroll_to_mark*(text_view, mark: pointer, within_margin: cdouble, use_align: bool, xalign, yalign: cdouble) {.importc, libgtk3.}
453 | # proc gtk_text_view_place_cursor_onscreen*(text_view: pointer): bool {.importc, libgtk3.}
454 | proc gtk_text_view_set_editable*(text_view: pointer, setting: bool) {.importc, libgtk3.}
455 |
456 | # proc gtk_text_buffer_new*(table: pointer): pointer {.importc, libgtk3.}
457 | proc gtk_text_buffer_set_text*(text_buffer: pointer, text: cstring, len: cint) {.importc, libgtk3.}
458 | proc gtk_text_buffer_get_text*(text_buffer: pointer, start, `end`: var GtkTextIter, include_hidden_chars: bool): cstring {.importc, libgtk3.}
459 | proc gtk_text_buffer_get_start_iter*(text_buffer: pointer, iter: var GtkTextIter) {.importc, libgtk3.}
460 | proc gtk_text_buffer_get_end_iter*(text_buffer: pointer, iter: var GtkTextIter) {.importc, libgtk3.}
461 | # proc gtk_text_buffer_add_mark*(buffer, mark: pointer, where: var GtkTextIter) {.importc, libgtk3.}
462 | proc gtk_text_buffer_get_insert*(buffer: pointer): pointer {.importc, libgtk3.}
463 | # proc gtk_text_buffer_get_iter_at_line*(buffer: pointer, iter: var GtkTextIter, line_number: cint) {.importc, libgtk3.}
464 | proc gtk_text_buffer_insert*(buffer: pointer, iter: var GtkTextIter, text: cstring, len: cint) {.importc, libgtk3.}
465 | proc gtk_text_buffer_get_selection_bounds*(buffer: pointer, start, `end`: var GtkTextIter): bool {.importc, libgtk3.}
466 | proc gtk_text_buffer_select_range*(buffer: pointer, ins, bound: var GtkTextIter) {.importc, libgtk3.}
467 | proc gtk_text_buffer_get_iter_at_offset*(buffer: pointer, iter: var GtkTextIter, char_offset: cint) {.importc, libgtk3.}
468 | proc gtk_text_buffer_get_iter_at_mark*(buffer: pointer, iter: var GtkTextIter, mark: pointer) {.importc, libgtk3.}
469 | proc gtk_text_buffer_create_tag*(buffer: pointer, tag_name, property_name, value: cstring, terminator: pointer): pointer {.importc, libgtk3.}
470 | proc gtk_text_buffer_insert_with_tags_by_name*(buffer: pointer, iter: var GtkTextIter, text: cstring, len: cint, first_tag_name: cstring, terminator: pointer) {.importc, libgtk3.}
471 |
472 | proc gtk_text_iter_get_offset*(iter: var GtkTextIter): cint {.importc, libgtk3.}
473 | # proc gtk_text_mark_new*(name: cstring, left_gravity: bool): pointer {.importc, libgtk3.}
474 |
475 | proc gtk_scrolled_window_new*(hadjustment, vadjustment: pointer): pointer {.importc, libgtk3.}
476 | proc gtk_scrolled_window_set_policy*(scrolled_window: pointer, hscrollbar_policy, vscrollbar_policy: cint) {.importc, libgtk3.}
477 | proc gtk_scrolled_window_get_hscrollbar*(scrolled_window: pointer): pointer {.importc, libgtk3.}
478 | proc gtk_scrolled_window_get_vscrollbar*(scrolled_window: pointer): pointer {.importc, libgtk3.}
479 | proc gtk_scrolled_window_get_hadjustment*(scrolled_window: pointer): pointer {.importc, libgtk3.}
480 | proc gtk_scrolled_window_get_vadjustment*(scrolled_window: pointer): pointer {.importc, libgtk3.}
481 | # proc gtk_scrolled_window_get_max_content_width*(scrolled_window: pointer): cint {.importc, libgtk3.}
482 | # proc gtk_scrolled_window_get_min_content_width*(scrolled_window: pointer): cint {.importc, libgtk3.}
483 | # proc gtk_scrolled_window_set_overlay_scrolling*(scrolled_window: pointer, overlay_scrolling: bool) {.importc, libgtk3.}
484 |
485 | proc gtk_frame_new*(label: cstring): pointer {.importc, libgtk3.}
486 | proc gtk_frame_set_label*(frame: pointer, label: cstring) {.importc, libgtk3.}
487 | proc gtk_frame_get_label_widget*(frame: pointer): pointer {.importc, libgtk3.}
488 |
489 | proc gtk_drawing_area_new*(): pointer {.importc, libgtk3.}
490 |
491 | proc gtk_adjustment_get_value*(adjustment: pointer): cdouble {.importc, libgtk3.}
492 | proc gtk_adjustment_set_value*(adjustment: pointer, value: cdouble) {.importc, libgtk3.}
493 | proc gtk_adjustment_set_upper*(adjustment: pointer, upper: cdouble) {.importc, libgtk3.}
494 | proc gtk_adjustment_get_upper*(adjustment: pointer): cdouble {.importc, libgtk3.}
495 | proc gtk_adjustment_set_page_size*(adjustment: pointer, page_size: cdouble) {.importc, libgtk3.}
496 | proc gtk_adjustment_get_page_size*(adjustment: pointer): cdouble {.importc, libgtk3.}
497 |
498 | proc gtk_drag_dest_set*(widget: pointer, flags: cint, targets: pointer, n_targets: cint, actions: cint) {.importc, libgtk3.}
499 |
500 | proc gdk_keyval_to_unicode*(keyval: cint): cint {.importc, libgtk3.}
501 |
502 | proc gdk_screen_get_default*(): pointer {.importc, libgtk3.}
503 | proc gdk_screen_get_primary_monitor*(screen: pointer): cint {.importc, libgtk3.}
504 | # proc gdk_screen_get_width*(screen: pointer): cint {.importc, libgtk3.}
505 | # proc gdk_screen_get_height*(screen: pointer): cint {.importc, libgtk3.}
506 | proc gdk_screen_get_monitor_workarea*(screen: pointer, monitor_num: cint, dest: var GdkRectangle) {.importc, libgtk3.}
507 |
508 | proc gtk_style_context_get_padding*(context: pointer, state: cint, padding: var GtkBorder) {.importc, libgtk3.}
509 | proc gtk_style_context_get_background_color*(context: pointer, state: cint, color: var GdkRGBA) {.importc, libgtk3.}
510 | proc gtk_style_context_get_color*(context: pointer, state: cint, color: var GdkRGBA) {.importc, libgtk3.}
511 | # proc gtk_style_context_get_font*(context: pointer, state: cint): pointer {.importc, libgtk3.}
512 |
513 | proc gtk_border_new*(): pointer {.importc, libgtk3.}
514 |
515 | proc gdk_threads_add_idle*(function, data: pointer): cint {.importc, libgtk3.}
516 |
517 | proc gtk_scrollbar_new*(orientation: cint, adjustment: pointer): pointer {.importc, libgtk3.}
518 |
519 | proc gtk_adjustment_new*(value, lower, upper, step_increment, page_increment, page_size: cdouble): pointer {.importc, libgtk3.}
520 |
521 | # proc gtk_selection_data_get_length*(selection_data: pointer): cint {.importc, libgtk3.}
522 | # proc gtk_selection_data_get_text*(selection_data: pointer): cstring {.importc, libgtk3.}
523 |
524 | proc gtk_selection_data_get_uris*(selection_data: pointer): ptr cstring {.importc, libgtk3.}
525 | proc g_filename_from_uri*(uri: pointer): cstring {.importc, libgtk3.}
526 |
527 | proc gtk_clipboard_get*(selection: pointer): pointer {.importc, libgtk3.}
528 | proc gtk_clipboard_set_text*(clipboard: pointer, text: cstring, len: cint) {.importc, libgtk3.}
529 | proc gtk_clipboard_request_text*(clipboard, callback, user_data: pointer) {.importc, libgtk3.}
530 | proc gtk_clipboard_store*(clipboard: pointer) {.importc, libgtk3.}
531 |
532 | proc gtk_accelerator_get_default_mod_mask*(): cint {.importc, libgtk3.}
533 |
534 | proc gtk_im_multicontext_new*(): pointer {.importc, libgtk3.}
535 | proc gtk_im_context_filter_keypress*(context: pointer, event: var GdkEventKey): bool {.importc, libgtk3.}
536 |
537 |
538 | # ----------------------------------------------------------------------------------------
539 | # Drawing Related Procs
540 | # ----------------------------------------------------------------------------------------
541 |
542 | proc gtk_widget_create_pango_layout*(widget: pointer, text: cstring): pointer {.importc, libgtk3.}
543 | proc gdk_cairo_set_source_rgba*(cr: pointer, rgba: var GdkRGBA) {.importc, libgtk3.}
544 | proc gdk_cairo_surface_create_from_pixbuf*(pixbuf: pointer, scale: cint, for_window: pointer): pointer {.importc, libgtk3.}
545 | proc gdk_pixbuf_new_from_file*(filename: cstring, error: pointer): pointer {.importc, libgtk3.}
546 | proc gdk_pixbuf_save*(pixbuf: pointer, filename, `type`: cstring, error: pointer, param5, param6, param7: cstring): bool {.importc, libgtk3.}
547 | proc gdk_pixbuf_get_from_surface*(surface: pointer, src_x, src_y, width, height: cint): pointer {.importc, libgtk3.}
548 | proc gdk_pixbuf_apply_embedded_orientation*(src: pointer): pointer {.importc, libgtk3.}
549 | # proc gdk_pixmap_create_from_data*(drawable, data: pointer, width, height, depth: cint, fg, bg: var GdkRGBA): pointer {.importc, libgtk3.}
550 |
551 | proc cairo_image_surface_create*(format: cint, width, height: cint): pointer {.importc, libgtk3.}
552 | # proc cairo_image_surface_create_for_data*(data: pointer, format: cairo_format_t, width, height, stride: cint): pointer {.importc, libgtk3.}
553 | proc cairo_image_surface_get_width*(surface: pointer): cint {.importc, libgtk3.}
554 | proc cairo_image_surface_get_height*(surface: pointer): cint {.importc, libgtk3.}
555 | proc cairo_image_surface_get_stride*(surface: pointer): cint {.importc, libgtk3.}
556 | proc cairo_image_surface_get_data*(surface: pointer): ptr UncheckedArray[byte] {.importc, libgtk3.}
557 | proc cairo_surface_flush*(surface: pointer) {.importc, libgtk3.}
558 | proc cairo_surface_mark_dirty*(surface: pointer) {.importc, libgtk3.}
559 | proc cairo_surface_destroy*(surface: pointer) {.importc, libgtk3.}
560 |
561 | # proc cairo_format_stride_for_width*(format: cairo_format_t, width: cint): cint {.importc, libgtk3.}
562 |
563 | proc cairo_create*(target: pointer): pointer {.importc, libgtk3.}
564 | proc cairo_destroy*(cr: pointer) {.importc, libgtk3.}
565 | proc cairo_get_target*(cr: pointer): pointer {.importc, libgtk3.}
566 | proc cairo_set_source_rgb*(cr: pointer, red, green, blue: cdouble) {.importc, libgtk3.}
567 | proc cairo_set_source_surface*(cr, surface: pointer, x, y: cdouble) {.importc, libgtk3.}
568 | proc cairo_get_source*(cr: pointer): pointer {.importc, libgtk3.}
569 | proc cairo_pattern_set_filter*(pattern: pointer, filter: cint) {.importc, libgtk3.}
570 | proc cairo_fill*(cr: pointer) {.importc, libgtk3.}
571 | proc cairo_stroke*(cr: pointer) {.importc, libgtk3.}
572 | proc cairo_rectangle*(cr: pointer, x, y, width, height: cdouble) {.importc, libgtk3.}
573 | proc cairo_arc*(cr: pointer, xc, yc, radius, angle1, angle2: cdouble) {.importc, libgtk3.}
574 | proc cairo_line_to*(cr: pointer, x, y: cdouble) {.importc, libgtk3.}
575 | proc cairo_move_to*(cr: pointer, x, y: cdouble) {.importc, libgtk3.}
576 | proc cairo_set_line_width*(cr: pointer, width: cdouble) {.importc, libgtk3.}
577 | # proc cairo_image_surface_create_from_png*(filename: cstring): pointer {.importc, libgtk3.}
578 | proc cairo_paint*(cr: pointer) {.importc, libgtk3.}
579 | proc cairo_scale*(cr: pointer, x, y: cdouble) {.importc, libgtk3.}
580 | proc cairo_translate*(cr: pointer, tx, ty: cdouble) {.importc, libgtk3.}
581 | # proc cairo_set_antialias*(cr: pointer, antialias: cint) {.importc, libgtk3.}
582 | proc cairo_save*(cr: pointer) {.importc, libgtk3.}
583 | proc cairo_restore*(cr: pointer) {.importc, libgtk3.}
584 |
585 | proc pango_cairo_show_layout*(cr, layout: pointer) {.importc, libgtk3.}
586 | proc pango_cairo_create_layout*(cr: pointer): pointer {.importc, libgtk3.}
587 | proc pango_layout_set_text*(layout: pointer, text: cstring, length: cint) {.importc, libgtk3.}
588 | proc pango_layout_get_pixel_size*(layout: pointer, width, height: var cint) {.importc, libgtk3.}
589 | proc pango_layout_set_font_description*(layout, desc: pointer) {.importc, libgtk3.}
590 | proc pango_font_description_new*(): pointer {.importc, libgtk3.}
591 | proc pango_font_description_free*(desc: pointer) {.importc, libgtk3.}
592 | proc pango_font_description_set_family*(desc: pointer, family: cstring) {.importc, libgtk3.}
593 | proc pango_font_description_set_size*(desc: pointer, size: cint) {.importc, libgtk3.}
594 | proc pango_font_description_set_weight*(desc: pointer, weight: cint) {.importc, libgtk3.}
595 | # proc pango_font_description_get_size*(desc: pointer): cint {.importc, libgtk3.}
596 | # proc pango_layout_set_markup*(layout: pointer, markup: cstring, length: cint) {.importc, libgtk3.}
597 | # proc pango_layout_new*(context: pointer): pointer {.importc, libgtk3.}
598 |
--------------------------------------------------------------------------------
/src/nigui/private/gtk3/platform_impl.nim:
--------------------------------------------------------------------------------
1 | # NiGui - GTK+ 3 platform-specific code - part 3
2 |
3 | # This file will be included in "nigui.nim".
4 |
5 | # Imports:
6 | # math, os, strutils, times are imported by nigui.nim
7 | import gtk3
8 | import tables
9 | import hashes
10 |
11 |
12 | # ----------------------------------------------------------------------------------------
13 | # Internal Things
14 | # ----------------------------------------------------------------------------------------
15 |
16 | const pFontSizeFactor = 700
17 |
18 | # needed to calculate clicks:
19 | var pLastMouseButtonDownControl: ControlImpl
20 | var pLastMouseButtonDownControlX: int
21 | var pLastMouseButtonDownControlY: int
22 |
23 | var pClipboardPtr: pointer
24 | var pClipboardText: string
25 | var pClipboardTextIsSet: bool
26 |
27 | proc pRaiseGError(error: ptr GError) =
28 | if error == nil:
29 | raiseError("Unknown error")
30 | raiseError($error.message)
31 |
32 | proc pColorToGdkRGBA(color: Color, rgba: var GdkRGBA) =
33 | rgba.red = color.red.float / 255
34 | rgba.green = color.green.float / 255
35 | rgba.blue = color.blue.float / 255
36 | rgba.alpha = color.alpha.float / 255
37 |
38 | proc pGdkRGBAToColor(rgba: var GdkRGBA): Color =
39 | result.red = uint8(rgba.red.float * 255)
40 | result.green = uint8(rgba.green.float * 255)
41 | result.blue = uint8(rgba.blue.float * 255)
42 | result.alpha = uint8(rgba.alpha.float * 255)
43 |
44 | proc pWindowDeleteSignal(widgetHandle, event, data: pointer): Gboolean {.cdecl.} =
45 | let window = cast[WindowImpl](data)
46 | window.closeClick()
47 | return true
48 |
49 | proc pWindowConfigureSignal(windowHandle, event, data: pointer): Gboolean {.cdecl.} =
50 | # called on resize and move
51 | let window = cast[WindowImpl](data)
52 | var x, y: cint
53 | gtk_window_get_position(window.fHandle, x, y)
54 | window.fX = x
55 | window.fY = y
56 | var width, height: cint
57 | gtk_window_get_size(window.fHandle, width, height)
58 | window.fWidth = width
59 | window.fHeight = height
60 | window.fClientWidth = width
61 | window.fClientHeight = height
62 | var event = new ResizeEvent
63 | event.window = window
64 | window.handleResizeEvent(event)
65 | window.triggerRelayout()
66 |
67 | proc pKeyvalToKey(keyval: cint): Key =
68 | result = case keyval
69 |
70 | # the following block is probably only correct for german keyboard layout
71 | of 39: Key_NumberSign
72 | of 42: Key_Plus
73 | of 58: Key_Point
74 | of 59: Key_Comma
75 | of 95: Key_Minus
76 |
77 | of 97: Key_A
78 | of 98: Key_B
79 | of 99: Key_C
80 | of 100: Key_D
81 | of 101: Key_E
82 | of 102: Key_F
83 | of 103: Key_G
84 | of 104: Key_H
85 | of 105: Key_I
86 | of 106: Key_J
87 | of 107: Key_K
88 | of 108: Key_L
89 | of 109: Key_M
90 | of 110: Key_N
91 | of 111: Key_O
92 | of 112: Key_P
93 | of 113: Key_Q
94 | of 114: Key_R
95 | of 115: Key_S
96 | of 116: Key_T
97 | of 117: Key_U
98 | of 118: Key_V
99 | of 119: Key_W
100 | of 120: Key_X
101 | of 121: Key_Y
102 | of 122: Key_Z
103 | of 228: Key_AE
104 | of 246: Key_OE
105 | of 252: Key_UE
106 | of 65027: Key_AltGr
107 | of 65106: Key_Circumflex
108 | of 65288: Key_Backspace
109 | of 65289: Key_Tab
110 | of 65293: Key_Return
111 | of 65299: Key_Pause
112 | of 65300: Key_ScrollLock
113 | of 65307: Key_Escape
114 | of 65379: Key_Insert
115 | of 65360: Key_Home
116 | of 65361: Key_Left
117 | of 65362: Key_Up
118 | of 65363: Key_Right
119 | of 65364: Key_Down
120 | of 65365: Key_PageUp
121 | of 65366: Key_PageDown
122 | of 65367: Key_End
123 | of 65377: Key_Print
124 | of 65383: Key_ContextMenu
125 | of 65407: Key_NumLock
126 | of 65421: Key_NumpadEnter
127 | of 65450: Key_NumpadMultiply
128 | of 65451: Key_NumpadAdd
129 | of 65452: Key_NumpadSeparator
130 | of 65453: Key_NumpadSubtract
131 | of 65454: Key_NumpadDecimal
132 | of 65455: Key_NumpadDivide
133 | of 65456: Key_Numpad0
134 | of 65457: Key_Numpad1
135 | of 65458: Key_Numpad2
136 | of 65459: Key_Numpad3
137 | of 65460: Key_Numpad4
138 | of 65461: Key_Numpad5
139 | of 65462: Key_Numpad6
140 | of 65463: Key_Numpad7
141 | of 65464: Key_Numpad8
142 | of 65465: Key_Numpad9
143 | of 65470: Key_F1
144 | of 65471: Key_F2
145 | of 65472: Key_F3
146 | of 65473: Key_F4
147 | of 65474: Key_F5
148 | of 65475: Key_F6
149 | of 65476: Key_F7
150 | of 65477: Key_F8
151 | of 65478: Key_F9
152 | of 65479: Key_F10
153 | of 65480: Key_F11
154 | of 65481: Key_F12
155 | of 65482: Key_F13
156 | of 65483: Key_F14
157 | of 65484: Key_F15
158 | of 65485: Key_F16
159 | of 65486: Key_F17
160 | of 65487: Key_F18
161 | of 65488: Key_F19
162 | of 65489: Key_F20
163 | of 65490: Key_F21
164 | of 65491: Key_F22
165 | of 65492: Key_F23
166 | of 65493: Key_F24
167 | of 65505: Key_ShiftL
168 | of 65506: Key_ShiftR
169 | of 65507: Key_ControlL
170 | of 65508: Key_ControlR
171 | of 65509: Key_CapsLock
172 | of 65513: Key_AltL
173 | of 65514: Key_AltR
174 | of 65515: Key_SuperL
175 | of 65516: Key_SuperR
176 | of 65535: Key_Delete
177 | else: cast[Key](keyval)
178 |
179 | proc pWindowKeyPressSignal(widget: pointer, event: var GdkEventKey, data: pointer): Gboolean {.cdecl.} =
180 | let window = cast[WindowImpl](data)
181 | window.fKeyPressed = pKeyvalToKey(event.keyval)
182 | internalKeyDown(window.fKeyPressed)
183 | if gtk_im_context_filter_keypress(window.fIMContext, event) and window.fKeyPressed == Key_None:
184 | return
185 | var evt = new KeyboardEvent
186 | evt.window = window
187 | evt.key = window.fKeyPressed
188 | if evt.key == Key_None:
189 | echo "Unknown key value: ", event.keyval
190 | return
191 | evt.character = $event.`string`
192 | evt.unicode = gdk_keyval_to_unicode(event.keyval)
193 | try:
194 | window.handleKeyDownEvent(evt)
195 | except:
196 | handleException()
197 | result = evt.handled
198 |
199 | proc pWindowKeyReleaseSignal(widget: pointer, event: var GdkEventKey): Gboolean {.cdecl.} =
200 | internalKeyUp(pKeyvalToKey(event.keyval))
201 |
202 | proc pControlKeyPressSignal(widget: pointer, event: var GdkEventKey, data: pointer): Gboolean {.cdecl.} =
203 | let control = cast[ControlImpl](data)
204 | let key = pKeyvalToKey(event.keyval)
205 | control.fKeyPressed = key
206 | if gtk_im_context_filter_keypress(control.fIMContext, event):
207 | return control.fKeyPressed == Key_None
208 | var evt = new KeyboardEvent
209 | evt.control = control
210 | evt.key = key
211 | if evt.key == Key_None:
212 | echo "Unknown key value: ", event.keyval
213 | return
214 | evt.character = $event.`string`
215 | evt.unicode = gdk_keyval_to_unicode(event.keyval)
216 | try:
217 | control.handleKeyDownEvent(evt)
218 | except:
219 | handleException()
220 | result = evt.handled
221 |
222 | proc pWindowIMContextCommitSignal(context: pointer, str: cstring, data: pointer) {.cdecl.} =
223 | let window = cast[WindowImpl](data)
224 | var evt = new KeyboardEvent
225 | evt.window = window
226 | evt.character = $str
227 | evt.unicode = evt.character.runeAt(0).toUpper().int
228 | evt.key = window.fKeyPressed
229 | window.fKeyPressed = Key_None
230 | try:
231 | window.handleKeyDownEvent(evt)
232 | except:
233 | handleException()
234 |
235 | proc pControlIMContextCommitSignal(context: pointer, str: cstring, data: pointer) {.cdecl.} =
236 | let control = cast[ControlImpl](data)
237 | var evt = new KeyboardEvent
238 | evt.control = control
239 | evt.character = $str
240 | evt.unicode = evt.character.runeAt(0).toUpper().int
241 | evt.key = control.fKeyPressed
242 | try:
243 | control.handleKeyDownEvent(evt)
244 | if evt.handled:
245 | control.fKeyPressed = Key_None
246 | except:
247 | handleException()
248 |
249 | method focus(control: ControlImpl) =
250 | gtk_widget_grab_focus(control.fHandle)
251 |
252 | method focus(control: NativeTextArea) =
253 | gtk_widget_grab_focus(control.fTextViewHandle)
254 |
255 | proc pDefaultControlButtonPressSignal(widget: pointer, event: var GdkEventButton, data: pointer): Gboolean {.cdecl.} =
256 | let control = cast[ControlImpl](data)
257 | let x = event.x.int
258 | let y = event.y.int
259 | var evt = new MouseEvent
260 | evt.control = control
261 | case event.button
262 | of 1: evt.button = MouseButton_Left
263 | of 2: evt.button = MouseButton_Middle
264 | of 3: evt.button = MouseButton_Right
265 | else: return # TODO
266 | evt.x = x
267 | evt.y = y
268 |
269 | try:
270 | control.handleMouseButtonDownEvent(evt)
271 | except:
272 | handleException()
273 |
274 | pLastMouseButtonDownControl = control
275 | pLastMouseButtonDownControlX = x
276 | pLastMouseButtonDownControlY = y
277 |
278 | proc pCustomControlButtonPressSignal(widget: pointer, event: var GdkEventButton, data: pointer): Gboolean {.cdecl.} =
279 | discard pDefaultControlButtonPressSignal(widget, event, data)
280 | let control = cast[ControlImpl](data)
281 | control.focus()
282 | result = true # Stop propagation, required to detect clicks
283 |
284 | proc pControlButtonReleaseSignal(widget: pointer, event: var GdkEventButton, data: pointer): Gboolean {.cdecl.} =
285 | let control = cast[ControlImpl](data)
286 | let x = event.x.int
287 | let y = event.y.int
288 | if not (x >= 0 and x < control.width and y >= 0 and y < control.height):
289 | return
290 | var evt = new MouseEvent
291 | evt.control = control
292 | case event.button
293 | of 1: evt.button = MouseButton_Left
294 | of 2: evt.button = MouseButton_Middle
295 | of 3: evt.button = MouseButton_Right
296 | else: return # TODO
297 | evt.x = x
298 | evt.y = y
299 | control.handleMouseButtonUpEvent(evt)
300 | if event.button == 1 and control == pLastMouseButtonDownControl and abs(x - pLastMouseButtonDownControlX) <= clickMaxXYMove and abs(y - pLastMouseButtonDownControlY) <= clickMaxXYMove:
301 | var clickEvent = new ClickEvent
302 | clickEvent.control = control
303 | try:
304 | control.handleClickEvent(clickEvent)
305 | except:
306 | handleException()
307 | # result = true # stop propagation
308 |
309 | proc pControlMotionNotifySignal(widget: pointer, event: var GdkEventMotion, data: pointer): Gboolean {.cdecl.} =
310 | let control = cast[ControlImpl](data)
311 | var evt = new MouseEvent
312 | evt.control = control
313 | evt.x = event.x.int
314 | evt.y = event.y.int
315 | control.handleMouseMoveEvent(evt)
316 |
317 | proc pControlEnterNotifySignal(widget: pointer, event: var GdkEventCrossing, data: pointer): Gboolean {.cdecl.} =
318 | let control = cast[ControlImpl](data)
319 | var evt = new MouseEvent
320 | evt.control = control
321 | control.handleMouseEnterEvent(evt)
322 |
323 | proc pControlLeaveNotifySignal(widget: pointer, event: var GdkEventCrossing, data: pointer): Gboolean {.cdecl.} =
324 | let control = cast[ControlImpl](data)
325 | var evt = new MouseEvent
326 | evt.control = control
327 | control.handleMouseLeaveEvent(evt)
328 |
329 | proc pControlChangedSignal(widget: pointer, data: pointer): Gboolean {.cdecl.} =
330 | let control = cast[TextBox](data)
331 | var evt = new TextChangeEvent
332 | evt.control = control
333 | try:
334 | control.handleTextChangeEvent(evt)
335 | except:
336 | handleException()
337 |
338 | # proc pTextAreaEndUserActionSignal(widget: pointer, data: pointer): Gboolean {.cdecl.} =
339 | # let control = cast[ControlImpl](data)
340 | # discard
341 |
342 | proc pSetDragDest(widget: pointer) =
343 | var target: GtkTargetEntry
344 | target.target = "text/uri-list"
345 | gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, target.addr, 1, GDK_ACTION_COPY)
346 |
347 | proc pCreateFont(fontFamily: string, fontSize: float, fontBold: bool): pointer =
348 | result = pango_font_description_new()
349 | pango_font_description_set_family(result, fontFamily)
350 | pango_font_description_set_size(result, cint(fontSize * pFontSizeFactor))
351 | if fontBold:
352 | pango_font_description_set_weight(result, 700)
353 | else:
354 | pango_font_description_set_weight(result, 400)
355 |
356 |
357 | # ----------------------------------------------------------------------------------------
358 | # App Procedures
359 | # ----------------------------------------------------------------------------------------
360 |
361 | var pQueue: int
362 |
363 | proc init(app: App) =
364 | gtk_init(nil, nil)
365 |
366 | pClipboardPtr = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)
367 |
368 | # Determine default styles:
369 | var window = gtk_window_new(GTK_WINDOW_TOPLEVEL)
370 | var context = gtk_widget_get_style_context(window)
371 | var rgba: GdkRGBA
372 | gtk_style_context_get_background_color(context, GTK_STATE_FLAG_NORMAL, rgba)
373 | app.defaultBackgroundColor = rgba.pGdkRGBAToColor()
374 | gtk_style_context_get_color(context, GTK_STATE_FLAG_NORMAL, rgba)
375 | app.defaultTextColor = rgba.pGdkRGBAToColor()
376 | gtk_widget_destroy(window)
377 |
378 | proc runMainLoop() = gtk_main()
379 |
380 | proc platformQuit() =
381 | gtk_main_quit()
382 |
383 | proc processEvents(app: App) =
384 | while gtk_events_pending() == 1:
385 | discard gtk_main_iteration()
386 |
387 | proc runQueuedFn(data: pointer): cint {.exportc.} =
388 | var fn = cast[ptr proc()](data)
389 | fn[]()
390 | freeShared(fn)
391 | dec pQueue
392 | return 0
393 |
394 | proc queueMain(app: App, fn: proc()) =
395 | inc pQueue
396 | var p = createShared(proc())
397 | p[] = fn
398 | discard gdk_threads_add_idle(cast[pointer](runQueuedFn), p)
399 |
400 | proc queued(app: App): int =
401 | return pQueue
402 |
403 | proc pClipboardTextReceivedFunc(clipboard: pointer, text: cstring, data: pointer): Gboolean {.cdecl.} =
404 | pClipboardText = $text # string needs to be copied
405 | pClipboardTextIsSet = true
406 |
407 | proc clipboardText(app: App): string =
408 | pClipboardTextIsSet = false
409 | gtk_clipboard_request_text(pClipboardPtr, cast[pointer](pClipboardTextReceivedFunc), nil)
410 | while not pClipboardTextIsSet:
411 | discard gtk_main_iteration()
412 | result = pClipboardText
413 |
414 | proc `clipboardText=`(app: App, text: string) =
415 | gtk_clipboard_set_text(pClipboardPtr, text, text.len.cint)
416 | gtk_clipboard_store(pClipboardPtr)
417 |
418 |
419 | # ----------------------------------------------------------------------------------------
420 | # Dialogs
421 | # ----------------------------------------------------------------------------------------
422 |
423 | proc alert(window: Window, message: string, title = "Message") =
424 | var dialog = gtk_dialog_new()
425 | gtk_window_set_title(dialog, title)
426 | gtk_window_resize(dialog, 200, 70)
427 | let contentArea = gtk_dialog_get_content_area(dialog)
428 | gtk_container_set_border_width(contentArea, 15)
429 | var label = gtk_label_new(message)
430 | gtk_widget_show(label)
431 | gtk_box_pack_start(contentArea, label, 0, 0, 0)
432 | let actionArea = gtk_dialog_get_action_area(dialog)
433 | gtk_button_box_set_layout(actionArea, GTK_BUTTONBOX_EXPAND)
434 | gtk_widget_set_margin_top(actionArea, 15)
435 | discard gtk_dialog_add_button(dialog, "OK", 1)
436 | if window != nil:
437 | gtk_window_set_transient_for(dialog, cast[WindowImpl](window).fHandle)
438 | discard gtk_dialog_run(dialog)
439 | gtk_widget_hide(dialog)
440 |
441 | method run*(dialog: OpenFileDialog) =
442 | dialog.files = @[]
443 | var chooser = gtk_file_chooser_dialog_new(dialog.title.cstring, nil, GTK_FILE_CHOOSER_ACTION_OPEN, "Cancel", GTK_RESPONSE_CANCEL, "Open", GTK_RESPONSE_ACCEPT, nil)
444 | discard gtk_file_chooser_set_current_folder(chooser, dialog.directory.cstring)
445 | gtk_file_chooser_set_select_multiple(chooser, dialog.multiple)
446 | let res = gtk_dialog_run(chooser)
447 | if res == GTK_RESPONSE_ACCEPT:
448 | let list = gtk_file_chooser_get_filenames(chooser)
449 | let count = g_slist_length(list)
450 | for i in 0..count - 1:
451 | dialog.files.add($g_slist_nth_data(list, i))
452 | gtk_widget_destroy(chooser)
453 |
454 | method run(dialog: SaveFileDialog) =
455 | var chooser = gtk_file_chooser_dialog_new(dialog.title.cstring, nil, GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_ACCEPT, nil)
456 | let res = gtk_dialog_run(chooser)
457 | discard gtk_file_chooser_set_current_folder(chooser, dialog.directory.cstring)
458 | if dialog.defaultName.len > 0:
459 | discard gtk_file_chooser_set_current_name(chooser, dialog.defaultName.cstring) # Issue: does not work
460 | if res == GTK_RESPONSE_ACCEPT:
461 | dialog.file = $gtk_file_chooser_get_filename(chooser)
462 | else:
463 | dialog.file = ""
464 | gtk_widget_destroy(chooser)
465 |
466 | method run*(dialog: SelectDirectoryDialog) =
467 | dialog.selectedDirectory = ""
468 | var chooser = gtk_file_chooser_dialog_new(dialog.title.cstring, nil, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, "Cancel", GTK_RESPONSE_CANCEL, "Select", GTK_RESPONSE_ACCEPT, nil)
469 | discard gtk_file_chooser_set_current_folder(chooser, dialog.startDirectory.cstring)
470 | let res = gtk_dialog_run(chooser)
471 | if res == GTK_RESPONSE_ACCEPT:
472 | dialog.selectedDirectory = $gtk_file_chooser_get_filename(chooser)
473 | gtk_widget_destroy(chooser)
474 |
475 |
476 | # ----------------------------------------------------------------------------------------
477 | # Timers
478 | # ----------------------------------------------------------------------------------------
479 |
480 | type TimerEntry = object
481 | timerInternalId: cint
482 | timerProc: TimerProc
483 | data: pointer
484 |
485 | var
486 | pTimers = initTable[int64, TimerEntry]()
487 | pNextTimerId: int = 1
488 |
489 | proc pTimerFunction(timer: Timer): Gboolean {.cdecl.} =
490 | let timerEntry = pTimers.getOrDefault(cast[int](timer))
491 | var event = new TimerEvent
492 | event.timer = timer
493 | event.data = timerEntry.data
494 | timerEntry.timerProc(event)
495 | pTimers.del(cast[int](timer))
496 | # result is false to stop timer
497 |
498 | proc pRepeatingTimerFunction(timer: Timer): Gboolean {.cdecl.} =
499 | let timerEntry = pTimers.getOrDefault(cast[int](timer))
500 | var event = new TimerEvent
501 | event.timer = timer
502 | event.data = timerEntry.data
503 | timerEntry.timerProc(event)
504 | result = true # repeat timer
505 |
506 | proc startTimer(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer =
507 | var timerEntry: TimerEntry
508 | timerEntry.timerInternalId = g_timeout_add(milliSeconds.cint, cast[pointer](pTimerFunction), cast[pointer](pNextTimerId))
509 | timerEntry.timerProc = timerProc
510 | timerEntry.data = data
511 | pTimers[pNextTimerId] = timerEntry
512 | result = cast[Timer](pNextTimerId)
513 | pNextTimerId.inc()
514 | if pNextTimerId == inactiveTimer:
515 | pNextTimerId.inc()
516 |
517 | proc startRepeatingTimer(milliSeconds: int, timerProc: TimerProc, data: pointer = nil): Timer =
518 | var timerEntry: TimerEntry
519 | timerEntry.timerInternalId = g_timeout_add(milliSeconds.cint, cast[pointer](pRepeatingTimerFunction), cast[pointer](pNextTimerId))
520 | timerEntry.timerProc = timerProc
521 | timerEntry.data = data
522 | pTimers[pNextTimerId] = timerEntry
523 | result = cast[Timer](pNextTimerId)
524 | pNextTimerId.inc()
525 | if pNextTimerId == inactiveTimer:
526 | pNextTimerId.inc()
527 |
528 | proc stop(timer: var Timer) =
529 | if cast[int](timer) != inactiveTimer:
530 | let timerEntry = pTimers.getOrDefault(cast[int](timer))
531 | pTimers.del(cast[int](timer))
532 | discard g_source_remove(timerEntry.timerInternalId)
533 | timer = cast[Timer](inactiveTimer)
534 |
535 |
536 | # ----------------------------------------------------------------------------------------
537 | # Canvas
538 | # ----------------------------------------------------------------------------------------
539 |
540 | proc pUpdateFont(canvas: Canvas) =
541 | let canvasImpl = cast[CanvasImpl](canvas)
542 | if canvasImpl.fFont != nil:
543 | pango_font_description_free(canvasImpl.fFont)
544 | canvasImpl.fFont = pCreateFont(canvas.fontFamily, canvas.fontSize, canvas.fontBold)
545 |
546 | method drawText(canvas: Canvas, text: string, x, y = 0) =
547 | let canvasImpl = cast[CanvasImpl](canvas)
548 | let cr = canvasImpl.fCairoContext
549 | if cr == nil:
550 | raiseError("Canvas is not in drawing state.")
551 | var rgba: GdkRGBA
552 | canvas.textColor.pColorToGdkRGBA(rgba)
553 | gdk_cairo_set_source_rgba(cr, rgba)
554 |
555 | var layout = pango_cairo_create_layout(cr)
556 | pango_layout_set_text(layout, text, text.len.cint)
557 |
558 | if canvasImpl.fFont == nil:
559 | canvas.pUpdateFont()
560 | pango_layout_set_font_description(layout, canvasImpl.fFont)
561 |
562 | cairo_save(cr)
563 | cairo_translate(cr, x.float, y.float)
564 | pango_cairo_show_layout(cr, layout)
565 | cairo_restore(cr)
566 | g_object_unref(layout)
567 |
568 | method drawLine(canvas: Canvas, x1, y1, x2, y2: int) =
569 | let cr = cast[CanvasImpl](canvas).fCairoContext
570 | if cr == nil:
571 | raiseError("Canvas is not in drawing state.")
572 | var rgba: GdkRGBA
573 | canvas.lineColor.pColorToGdkRGBA(rgba)
574 | gdk_cairo_set_source_rgba(cr, rgba)
575 | cairo_move_to(cr, x1.float, y1.float)
576 | cairo_line_to(cr, x2.float, y2.float)
577 | cairo_set_line_width(cr, canvas.lineWidth)
578 | cairo_stroke(cr)
579 |
580 | method drawRectArea(canvas: Canvas, x, y, width, height: int) =
581 | let cr = cast[CanvasImpl](canvas).fCairoContext
582 | if cr == nil:
583 | raiseError("Canvas is not in drawing state.")
584 | var rgba: GdkRGBA
585 | canvas.areaColor.pColorToGdkRGBA(rgba)
586 | gdk_cairo_set_source_rgba(cr, rgba)
587 | cairo_rectangle(cr, x.float, y.float, width.float, height.float)
588 | cairo_fill(cr)
589 |
590 | method drawRectOutline(canvas: Canvas, x, y, width, height: int) =
591 | let cr = cast[CanvasImpl](canvas).fCairoContext
592 | if cr == nil:
593 | raiseError("Canvas is not in drawing state.")
594 | var rgba: GdkRGBA
595 | canvas.lineColor.pColorToGdkRGBA(rgba)
596 | gdk_cairo_set_source_rgba(cr, rgba)
597 | cairo_rectangle(cr, x.float, y.float, width.float, height.float)
598 | cairo_set_line_width(cr, canvas.lineWidth)
599 | cairo_stroke(cr)
600 |
601 | method drawEllipseArea(canvas: Canvas, x, y, width, height: int) =
602 | let cr = cast[CanvasImpl](canvas).fCairoContext
603 | if cr == nil:
604 | raiseError("Canvas is not in drawing state.")
605 | var rgba: GdkRGBA
606 | canvas.areaColor.pColorToGdkRGBA(rgba)
607 | gdk_cairo_set_source_rgba(cr, rgba)
608 | cairo_save(cr)
609 | let centerX = x.float + width.float / 2
610 | let centerY = y.float + height.float / 2
611 | cairo_translate(cr, centerX, centerY)
612 | cairo_scale(cr, width.float / 2, height.float / 2)
613 | cairo_arc(cr, 0, 0, 1, 0, 2 * PI)
614 | cairo_fill(cr)
615 | cairo_restore(cr)
616 |
617 | method drawEllipseOutline(canvas: Canvas, x, y, width, height: int) =
618 | let cr = cast[CanvasImpl](canvas).fCairoContext
619 | if cr == nil:
620 | raiseError("Canvas is not in drawing state.")
621 | var rgba: GdkRGBA
622 | canvas.lineColor.pColorToGdkRGBA(rgba)
623 | gdk_cairo_set_source_rgba(cr, rgba)
624 | cairo_save(cr)
625 | let centerX = x.float + width.float / 2
626 | let centerY = y.float + height.float / 2
627 | cairo_translate(cr, centerX, centerY)
628 | cairo_scale(cr, width.float / 2, height.float / 2)
629 | cairo_arc(cr, 0, 0, 1, 0, 2 * PI)
630 | cairo_set_line_width(cr, canvas.lineWidth / width.float * 2)
631 | # problem: width of horizontal line and vertical line is not the same
632 | cairo_stroke(cr)
633 | cairo_restore(cr)
634 |
635 | method drawArcOutline(canvas: Canvas, centerX, centerY: int, radius, startAngle, sweepAngle: float) =
636 | let cr = cast[CanvasImpl](canvas).fCairoContext
637 | if cr == nil:
638 | raiseError("Canvas is not in drawing state.")
639 | var rgba: GdkRGBA
640 | canvas.lineColor.pColorToGdkRGBA(rgba)
641 | gdk_cairo_set_source_rgba(cr, rgba)
642 | cairo_arc(cr, centerX.float, centerY.float, radius, startAngle, sweepAngle)
643 | cairo_set_line_width(cr, canvas.lineWidth)
644 | cairo_stroke(cr)
645 |
646 | method drawImage(canvas: Canvas, image: Image, x, y = 0, width, height = -1) =
647 | let cr = cast[CanvasImpl](canvas).fCairoContext
648 | if cr == nil:
649 | raiseError("Canvas is not in drawing state.")
650 | let imageCanvas = cast[CanvasImpl](image.canvas)
651 | if imageCanvas.fSurface == nil:
652 | raiseError("Image is not initialized.")
653 | var drawWith = image.width
654 | var drawHeight = image.height
655 | if width != -1:
656 | drawWith = width
657 | drawHeight = int(drawHeight * drawWith / image.width)
658 | if height != -1:
659 | drawHeight = height
660 | if drawWith == image.width and drawHeight == image.height:
661 | cairo_set_source_surface(cr, imageCanvas.fSurface, x.cdouble, y.cdouble)
662 | cairo_paint(cr)
663 | else:
664 | cairo_save(cr)
665 | cairo_translate(cr, x.cdouble, y.cdouble)
666 | cairo_scale(cr, drawWith / image.width, drawHeight / image.height)
667 | cairo_set_source_surface(cr, imageCanvas.fSurface, 0, 0)
668 | case canvas.interpolationMode:
669 | of InterpolationMode_Default: discard
670 | of InterpolationMode_NearestNeighbor: cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST)
671 | of InterpolationMode_Bilinear: cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR)
672 | cairo_paint(cr)
673 | cairo_restore(cr)
674 |
675 | method setPixel(canvas: Canvas, x, y: int, color: Color) =
676 | let canvasImpl = cast[CanvasImpl](canvas)
677 | let cr = canvasImpl.fCairoContext
678 | if cr == nil:
679 | raiseError("Canvas is not in drawing state.")
680 | if canvasImpl.fData == nil:
681 | # For a Canvas of a Control we can't access the memory directly, so draw a rectangle (slower)
682 | var rgba: GdkRGBA
683 | color.pColorToGdkRGBA(rgba)
684 | gdk_cairo_set_source_rgba(cr, rgba)
685 | cairo_rectangle(cr, x.float, y.float, 1, 1)
686 | cairo_fill(cr)
687 | else:
688 | # For a Canvas of an Image we can write in the memory directly (faster)
689 | if x < 0 or y < 0 or x >= canvas.width or y >= canvas.height:
690 | raiseError("Pixel is out of range.")
691 | cairo_surface_flush(canvasImpl.fSurface)
692 | let i = y * canvasImpl.fStride + x * 4
693 | canvasImpl.fData[i + 0] = color.blue
694 | canvasImpl.fData[i + 1] = color.green
695 | canvasImpl.fData[i + 2] = color.red
696 | canvasImpl.fData[i + 3] = 255
697 | cairo_surface_mark_dirty(canvasImpl.fSurface)
698 |
699 | method `fontFamily=`(canvas: CanvasImpl, fontFamily: string) =
700 | procCall canvas.Canvas.`fontFamily=`(fontFamily)
701 | canvas.fFont = nil
702 |
703 | method `fontSize=`(canvas: CanvasImpl, fontSize: float) =
704 | procCall canvas.Canvas.`fontSize=`(fontSize)
705 | canvas.fFont = nil
706 |
707 | method `fontBold=`(canvas: CanvasImpl, fontBold: bool) =
708 | procCall canvas.Canvas.`fontBold=`(fontBold)
709 | canvas.fFont = nil
710 |
711 | method getTextLineWidth(canvas: CanvasImpl, text: string): int =
712 | if canvas.fCairoContext == nil:
713 | raiseError("Canvas is not in drawing state.")
714 | var layout = pango_cairo_create_layout(canvas.fCairoContext)
715 | pango_layout_set_text(layout, text, text.len.cint)
716 | if canvas.fFont == nil:
717 | canvas.pUpdateFont()
718 | pango_layout_set_font_description(layout, canvas.fFont)
719 | var width: cint
720 | var height: cint
721 | pango_layout_get_pixel_size(layout, width, height)
722 | result = width + 2
723 | g_object_unref(layout)
724 |
725 | method getTextLineHeight(canvas: CanvasImpl): int =
726 | if canvas.fCairoContext == nil:
727 | raiseError("Canvas is not in drawing state.")
728 | var layout = pango_cairo_create_layout(canvas.fCairoContext)
729 | pango_layout_set_text(layout, "a", 1)
730 | if canvas.fFont == nil:
731 | canvas.pUpdateFont()
732 | pango_layout_set_font_description(layout, canvas.fFont)
733 | var width: cint
734 | var height: cint
735 | pango_layout_get_pixel_size(layout, width, height)
736 | result = height
737 | g_object_unref(layout)
738 |
739 |
740 | # ----------------------------------------------------------------------------------------
741 | # Image
742 | # ----------------------------------------------------------------------------------------
743 |
744 | method resize(image: Image, width, height: int) =
745 | let canvas = cast[CanvasImpl](image.fCanvas)
746 | if canvas.fSurface != nil:
747 | cairo_surface_destroy(canvas.fSurface)
748 | if canvas.fCairoContext != nil:
749 | cairo_destroy(canvas.fCairoContext)
750 | canvas.fSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width.cint, height.cint)
751 | canvas.fCairoContext = cairo_create(canvas.fSurface)
752 | canvas.fData = cairo_image_surface_get_data(canvas.fSurface)
753 | canvas.fStride = cairo_image_surface_get_stride(canvas.fSurface)
754 | image.canvas.fWidth = width
755 | image.canvas.fHeight = height
756 |
757 | method loadFromFile(image: Image, filePath: string) =
758 | let canvas = cast[CanvasImpl](image.fCanvas)
759 | if canvas.fSurface != nil:
760 | cairo_surface_destroy(canvas.fSurface)
761 | if canvas.fCairoContext != nil:
762 | cairo_destroy(canvas.fCairoContext)
763 | image.canvas.fWidth = 0
764 | image.canvas.fHeight = 0
765 | var error: ptr GError
766 | var pixbuf = gdk_pixbuf_new_from_file(filePath, error.addr)
767 | if pixbuf == nil:
768 | pRaiseGError(error)
769 | defer: g_object_unref(pixbuf)
770 | var pixbufRotated = gdk_pixbuf_apply_embedded_orientation(pixbuf)
771 | defer: g_object_unref(pixbufRotated)
772 | canvas.fSurface = gdk_cairo_surface_create_from_pixbuf(pixbufRotated, 1, nil)
773 | canvas.fCairoContext = cairo_create(canvas.fSurface)
774 | canvas.fData = cairo_image_surface_get_data(canvas.fSurface)
775 | canvas.fStride = cairo_image_surface_get_stride(canvas.fSurface)
776 | image.canvas.fWidth = cairo_image_surface_get_width(canvas.fSurface)
777 | image.canvas.fHeight = cairo_image_surface_get_height(canvas.fSurface)
778 |
779 | method saveToBitmapFile(image: Image, filePath: string) =
780 | let canvas = cast[CanvasImpl](image.fCanvas)
781 | var pixbuf = gdk_pixbuf_get_from_surface(canvas.fSurface, 0, 0, image.width.cint, image.height.cint)
782 | defer: g_object_unref(pixbuf)
783 | var error: ptr GError
784 | if not gdk_pixbuf_save(pixbuf, filePath, "bmp", error.addr, nil, nil, nil):
785 | pRaiseGError(error)
786 |
787 | method saveToPngFile(image: Image, filePath: string) =
788 | let canvas = cast[CanvasImpl](image.fCanvas)
789 | var pixbuf = gdk_pixbuf_get_from_surface(canvas.fSurface, 0, 0, image.width.cint, image.height.cint)
790 | defer: g_object_unref(pixbuf)
791 | var error: ptr GError
792 | if not gdk_pixbuf_save(pixbuf, filePath, "png", error.addr, nil, nil, nil):
793 | pRaiseGError(error)
794 |
795 | method saveToJpegFile(image: Image, filePath: string, quality = 80) =
796 | let canvas = cast[CanvasImpl](image.fCanvas)
797 | var pixbuf = gdk_pixbuf_get_from_surface(canvas.fSurface, 0, 0, image.width.cint, image.height.cint)
798 | defer: g_object_unref(pixbuf)
799 | var error: ptr GError
800 | if not gdk_pixbuf_save(pixbuf, filePath, "jpeg", error.addr, "quality", ($quality).cstring, nil):
801 | pRaiseGError(error)
802 |
803 | method beginPixelDataAccess(image: Image): ptr UncheckedArray[byte] =
804 | let canvas = cast[CanvasImpl](image.canvas)
805 | result = canvas.fData
806 |
807 | method endPixelDataAccess(image: Image) =
808 | let canvas = cast[CanvasImpl](image.canvas)
809 | cairo_surface_mark_dirty(canvas.fSurface)
810 |
811 |
812 | # ----------------------------------------------------------------------------------------
813 | # Window
814 | # ----------------------------------------------------------------------------------------
815 |
816 | proc pWindowDragDataReceivedSignal(widget, context: pointer, x, y: cint, data: pointer, info, time: cint, user_data: pointer) {.cdecl.} =
817 | let window = cast[WindowImpl](user_data)
818 | var files: seq[string] = @[]
819 | var p = gtk_selection_data_get_uris(data)
820 | while p[] != nil:
821 | files.add($g_filename_from_uri(p[]))
822 | p = cast[ptr cstring](cast[int](p) + 8)
823 | var event = new DropFilesEvent
824 | event.window = window
825 | event.files = files
826 | window.handleDropFilesEvent(event)
827 |
828 | proc pMainScrollbarDraw(widget: pointer, cr: pointer, data: pointer): Gboolean {.cdecl.} =
829 | # This proc is there to get the scrollbar size
830 | if fScrollbarSize == -1:
831 | var scrollbar = gtk_scrolled_window_get_hscrollbar(widget)
832 | var allocation: GdkRectangle
833 | gtk_widget_get_allocation(scrollbar, allocation)
834 | gtk_scrolled_window_set_policy(widget, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC)
835 | fScrollbarSize = allocation.height
836 | for window in windowList:
837 | if window.control != nil:
838 | window.control.triggerRelayoutDownwards()
839 |
840 | proc pWindowStateEventSignal(widget: pointer, event: var GdkEventWindowState, user_data: pointer): Gboolean {.cdecl.} =
841 | let window = cast[WindowImpl](user_data)
842 | window.fMinimized = (event.new_window_state and GDK_WINDOW_STATE_ICONIFIED) == GDK_WINDOW_STATE_ICONIFIED
843 |
844 | proc pWindowFocusOutEventSignal(widget: pointer, event: var GdkEventFocus, user_data: pointer): Gboolean {.cdecl.} =
845 | internalAllKeysUp()
846 |
847 | proc init(window: WindowImpl) =
848 | if pClipboardPtr == nil:
849 | gtk_init(nil, nil)
850 | raiseError("You need to call 'app.init()' at first.")
851 | window.fHandle = gtk_window_new(GTK_WINDOW_TOPLEVEL)
852 | window.fInnerHandle = gtk_scrolled_window_new(nil, nil)
853 | gtk_widget_show(window.fInnerHandle)
854 | gtk_container_add(window.fHandle, window.fInnerHandle)
855 | window.Window.init()
856 | discard g_signal_connect_data(window.fHandle, "delete-event", cast[pointer](pWindowDeleteSignal), cast[pointer](window))
857 | discard g_signal_connect_data(window.fHandle, "configure-event", cast[pointer](pWindowConfigureSignal), cast[pointer](window))
858 | discard g_signal_connect_data(window.fHandle, "key-press-event", cast[pointer](pWindowKeyPressSignal), cast[pointer](window))
859 | discard g_signal_connect_data(window.fHandle, "key-release-event", cast[pointer](pWindowKeyReleaseSignal), cast[pointer](window))
860 | discard g_signal_connect_data(window.fHandle, "window-state-event", cast[pointer](pWindowStateEventSignal), cast[pointer](window))
861 | discard g_signal_connect_data(window.fHandle, "focus-out-event", cast[pointer](pWindowFocusOutEventSignal), cast[pointer](window))
862 |
863 | # Enable drag and drop of files:
864 | pSetDragDest(window.fHandle)
865 | discard g_signal_connect_data(window.fHandle, "drag-data-received", cast[pointer](pWindowDragDataReceivedSignal), cast[pointer](window))
866 |
867 | if fScrollbarSize == -1:
868 | gtk_scrolled_window_set_policy(window.fInnerHandle, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS)
869 | discard g_signal_connect_data(window.fInnerHandle, "draw", cast[pointer](pMainScrollbarDraw), nil)
870 |
871 | window.fIMContext = gtk_im_multicontext_new()
872 | discard g_signal_connect_data(window.fIMContext, "commit", cast[pointer](pWindowIMContextCommitSignal), cast[pointer](window))
873 |
874 | method destroy(window: WindowImpl) =
875 | procCall window.Window.destroy()
876 | gtk_widget_destroy(window.fHandle)
877 | # this destroys also child widgets
878 | window.fHandle = nil
879 |
880 | method `visible=`(window: WindowImpl, visible: bool) =
881 | procCall window.Window.`visible=`(visible)
882 | if visible:
883 | # gtk_window_deiconify(window.fHandle)
884 | # gtk_widget_show(window.fHandle)
885 | gtk_window_present(window.fHandle)
886 | while fScrollbarSize == -1:
887 | discard gtk_main_iteration()
888 | else:
889 | gtk_widget_hide(window.fHandle)
890 | app.processEvents()
891 |
892 | method showModal(window: WindowImpl, parent: Window) =
893 | # Overwrite base method
894 | gtk_window_set_modal(window.fHandle, 1)
895 | gtk_window_set_transient_for(window.fHandle, cast[WindowImpl](parent).fHandle)
896 | gtk_window_set_type_hint(window.fHandle, GDK_WINDOW_TYPE_HINT_DIALOG)
897 | window.visible = true
898 |
899 | method minimize(window: WindowImpl) =
900 | procCall window.Window.minimize()
901 | gtk_window_iconify(window.fHandle)
902 |
903 | method `alwaysOnTop=`(window: WindowImpl, alwaysOnTop: bool) =
904 | procCall window.Window.`alwaysOnTop=`(alwaysOnTop)
905 | gtk_window_set_keep_above(window.fHandle, alwaysOnTop)
906 |
907 | method `width=`*(window: WindowImpl, width: int) =
908 | procCall window.Window.`width=`(width)
909 | gtk_window_set_default_size(window.fHandle, window.width.cint, window.height.cint)
910 | window.fClientWidth = window.width
911 |
912 | method `height=`*(window: WindowImpl, height: int) =
913 | procCall window.Window.`height=`(height)
914 | gtk_window_set_default_size(window.fHandle, window.width.cint, window.height.cint)
915 | window.fClientHeight = window.height
916 |
917 | proc pUpdateMinSize(window: WindowImpl) =
918 | var geometry: GdkGeometry
919 | geometry.min_width = window.minWidth.cint
920 | geometry.min_height = window.minHeight.cint
921 | gtk_window_set_geometry_hints(window.fHandle, nil, geometry, GDK_HINT_MIN_SIZE)
922 |
923 | method `minWidth=`(window: WindowImpl, minWidth: int) =
924 | procCall window.Window.`minWidth=`(minWidth)
925 | pUpdateMinSize(window)
926 |
927 | method `minHeight=`(window: WindowImpl, minHeight: int) =
928 | procCall window.Window.`minHeight=`(minHeight)
929 | pUpdateMinSize(window)
930 |
931 | method `resizable=`(window: WindowImpl, resizable: bool) =
932 | procCall window.Window.`resizable=`(resizable)
933 | gtk_window_set_resizable(window.fHandle, resizable)
934 |
935 | proc pUpdatePosition(window: WindowImpl) = gtk_window_move(window.fHandle, window.x.cint, window.y.cint)
936 |
937 | method `x=`(window: WindowImpl, x: int) =
938 | procCall window.Window.`x=`(x)
939 | window.pUpdatePosition()
940 |
941 | method `y=`(window: WindowImpl, y: int) =
942 | procCall window.Window.`y=`(y)
943 | window.pUpdatePosition()
944 |
945 | method centerOnScreen(window: WindowImpl) =
946 | let screen = gdk_screen_get_default()
947 | let monitor = gdk_screen_get_primary_monitor(screen)
948 | var rect: GdkRectangle
949 | gdk_screen_get_monitor_workarea(screen, monitor, rect)
950 | window.fX = rect.x + (rect.width - window.width) div 2
951 | window.fY = rect.y + (rect.height - window.height) div 2
952 | window.pUpdatePosition()
953 |
954 | method `title=`(window: WindowImpl, title: string) =
955 | procCall window.Window.`title=`(title)
956 | gtk_window_set_title(window.fHandle, window.title.cstring)
957 |
958 | method `control=`(window: WindowImpl, control: Control) =
959 | # Overwrite base method
960 | procCall window.Window.`control=`(control)
961 | gtk_container_add(window.fInnerHandle, cast[ControlImpl](control).fHandle)
962 |
963 | method mousePosition(window: Window): tuple[x, y: int] =
964 | var x, y: cint
965 | gtk_widget_get_pointer(cast[WindowImpl](window).fHandle, x, y)
966 | result.x = x
967 | result.y = y
968 |
969 | method `iconPath=`(window: WindowImpl, iconPath: string) =
970 | procCall window.Window.`iconPath=`(iconPath)
971 | if not gtk_window_set_icon_from_file(window.fHandle, iconPath, nil):
972 | if not fileExists(iconPath):
973 | raiseError("Failed to load image from file '" & iconPath & "': File does not exist")
974 | else:
975 | raiseError("Failed to load image from file '" & iconPath & "'")
976 |
977 |
978 | # ----------------------------------------------------------------------------------------
979 | # Control
980 | # ----------------------------------------------------------------------------------------
981 |
982 | method pUpdateScrollBar(control: ControlImpl) {.base.}
983 |
984 | method triggerRelayout(control: ControlImpl) =
985 | procCall control.Control.triggerRelayout()
986 | control.pUpdateScrollBar()
987 |
988 | proc pControlDrawSignal(widget: pointer, cr: pointer, data: pointer): Gboolean {.cdecl.} =
989 | let control = cast[ControlImpl](data)
990 | var event = new DrawEvent
991 | event.control = control
992 | var canvas = cast[CanvasImpl](control.canvas)
993 | if canvas == nil:
994 | canvas = newCanvas(control)
995 | canvas.fCairoContext = cr
996 | try:
997 | control.handleDrawEvent(event)
998 | except:
999 | handleException()
1000 | canvas.fCairoContext = nil
1001 |
1002 | proc pControlScollXSignal(adjustment: pointer, data: pointer) {.cdecl.} =
1003 | let control = cast[ControlImpl](data)
1004 | control.fXScrollPos = gtk_adjustment_get_value(adjustment).int
1005 | control.forceRedraw()
1006 |
1007 | proc pControlScollYSignal(adjustment: pointer, data: pointer) {.cdecl.} =
1008 | let control = cast[ControlImpl](data)
1009 | control.fYScrollPos = gtk_adjustment_get_value(adjustment).int
1010 | control.forceRedraw()
1011 |
1012 | proc pUpdateFont(control: ControlImpl) =
1013 | var font = pCreateFont(control.fontFamily, control.fontSize, control.fontBold)
1014 | gtk_widget_modify_font(control.fHandle, font)
1015 | pango_font_description_free(font)
1016 |
1017 | method pAddButtonPressEvent(control: ControlImpl) {.base.} =
1018 | gtk_widget_add_events(control.fHandle, GDK_BUTTON_PRESS_MASK)
1019 | discard g_signal_connect_data(control.fHandle, "button-press-event", cast[pointer](pCustomControlButtonPressSignal), cast[pointer](control))
1020 |
1021 | method pAddKeyPressEvent(control: ControlImpl) {.base.} =
1022 | discard g_signal_connect_data(control.fHandle, "key-press-event", cast[pointer](pControlKeyPressSignal), cast[pointer](control))
1023 |
1024 | proc init(control: ControlImpl) =
1025 |
1026 | if control.fHandle == nil:
1027 | # Direct instance of ControlImpl:
1028 | control.fHandle = gtk_layout_new(nil, nil)
1029 | discard g_signal_connect_data(control.fHandle, "draw", cast[pointer](pControlDrawSignal), cast[pointer](control))
1030 | gtk_widget_add_events(control.fHandle, GDK_KEY_PRESS_MASK)
1031 |
1032 | control.pAddButtonPressEvent()
1033 | control.pAddKeyPressEvent()
1034 |
1035 | gtk_widget_add_events(control.fHandle, GDK_BUTTON_RELEASE_MASK)
1036 | discard g_signal_connect_data(control.fHandle, "button-release-event", cast[pointer](pControlButtonReleaseSignal), cast[pointer](control))
1037 |
1038 | gtk_widget_add_events(control.fHandle, GDK_POINTER_MOTION_MASK)
1039 | discard g_signal_connect_data(control.fHandle, "motion-notify-event", cast[pointer](pControlMotionNotifySignal), cast[pointer](control))
1040 |
1041 | gtk_widget_add_events(control.fHandle, GDK_ENTER_NOTIFY_MASK)
1042 | discard g_signal_connect_data(control.fHandle, "enter-notify-event", cast[pointer](pControlEnterNotifySignal), cast[pointer](control))
1043 |
1044 | gtk_widget_add_events(control.fHandle, GDK_LEAVE_NOTIFY_MASK)
1045 | discard g_signal_connect_data(control.fHandle, "leave-notify-event", cast[pointer](pControlLeaveNotifySignal), cast[pointer](control))
1046 |
1047 | control.fIMContext = gtk_im_multicontext_new()
1048 | discard g_signal_connect_data(control.fIMContext, "commit", cast[pointer](pControlIMContextCommitSignal), cast[pointer](control))
1049 |
1050 | procCall control.Control.init()
1051 |
1052 | method destroy(control: ControlImpl) =
1053 | procCall control.Control.destroy()
1054 | gtk_widget_destroy(control.fHandle)
1055 | # this destroys also child widgets
1056 |
1057 | method `visible=`(control: ControlImpl, visible: bool) =
1058 | procCall control.Control.`visible=`(visible)
1059 | if visible:
1060 | gtk_widget_show(control.fHandle)
1061 | else:
1062 | gtk_widget_hide(control.fHandle)
1063 |
1064 | # proc dummy(widget: pointer, event: var GdkEventButton, data: pointer): Gboolean {.cdecl.} =
1065 | # echo "dummy"
1066 | # result = true # Stop propagation
1067 |
1068 | method pUpdateScrollBar(control: ControlImpl) =
1069 | if control.fScrollableWidth == -1 and control.fScrollableHeight == -1:
1070 | return
1071 | echo "control.pUpdateScrollBar"
1072 | if control.fHScrollbar == nil:
1073 | if fScrollbarSize == -1:
1074 | return
1075 | # Init scrolling:
1076 | # echo "fScrollbarSize ", fScrollbarSize
1077 | control.fHAdjust = gtk_adjustment_new(0, 0, 0, 10, 10, 0)
1078 | control.fVAdjust = gtk_adjustment_new(0, 0, 0, 10, 10, 0)
1079 | control.fHScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, control.fHAdjust)
1080 | control.fVScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, control.fVAdjust)
1081 | gtk_container_add(control.fHandle, control.fHScrollbar)
1082 | gtk_container_add(control.fHandle, control.fVScrollbar)
1083 | discard g_signal_connect_data(control.fHAdjust, "value-changed", cast[pointer](pControlScollXSignal), cast[pointer](control))
1084 | discard g_signal_connect_data(control.fVAdjust, "value-changed", cast[pointer](pControlScollYSignal), cast[pointer](control))
1085 |
1086 | # The dead corner is an area which just needs to be covered with a control without function and the default background color
1087 | control.fDeadCornerHandle = gtk_label_new("")
1088 | # control.fDeadCornerHandle = gtk_fixed_new()
1089 | gtk_container_add(control.fHandle, control.fDeadCornerHandle)
1090 | var rgba: GdkRGBA
1091 | pColorToGdkRGBA(app.defaultBackgroundColor, rgba)
1092 | gtk_widget_override_background_color(control.fDeadCornerHandle, GTK_STATE_FLAG_NORMAL, rgba)
1093 | gtk_widget_set_size_request(control.fDeadCornerHandle, fScrollbarSize.cint, fScrollbarSize.cint)
1094 |
1095 | # Prevent that a click on the dead corner triggers the onClick event of the Control:
1096 | # gtk_widget_add_events(control.fDeadCornerHandle, GDK_BUTTON_PRESS_MASK)
1097 | # discard g_signal_connect_data(control.fDeadCornerHandle, "button-press-event", dummy, nil)
1098 | # gtk_widget_add_events(control.fDeadCornerHandle, GDK_BUTTON_RELEASE_MASK)
1099 | # discard g_signal_connect_data(control.fDeadCornerHandle, "button-release-event", dummy, nil)
1100 | # TODO: does not work. try EventBox
1101 |
1102 | # Calculation of scrollbar settings:
1103 |
1104 | control.fXScrollEnabled = false
1105 | control.fYScrollEnabled = false
1106 |
1107 | if control.scrollableWidth > control.width:
1108 | control.fXScrollEnabled = true
1109 | if control.scrollableHeight > control.height:
1110 | control.fYScrollEnabled = true
1111 |
1112 | if control.fXScrollEnabled and not control.fYScrollEnabled and control.scrollableHeight > control.height - fScrollbarSize:
1113 | control.fYScrollEnabled = true
1114 | if control.fYScrollEnabled and not control.fXScrollEnabled and control.scrollableWidth > control.width - fScrollbarSize:
1115 | control.fXScrollEnabled = true
1116 |
1117 | # Update horizontal scrollbar:
1118 | if control.fXScrollEnabled:
1119 | var upper = control.scrollableWidth
1120 | var size = control.width
1121 | if control.fYScrollEnabled:
1122 | upper.inc(fScrollbarSize)
1123 | size.dec(fScrollbarSize)
1124 | gtk_adjustment_set_upper(control.fHAdjust, upper.cdouble)
1125 | let value = gtk_adjustment_get_value(control.fHAdjust).int
1126 | let maxValue = upper - control.width
1127 | if value > maxValue:
1128 | gtk_adjustment_set_value(control.fHAdjust, maxValue.float)
1129 | gtk_adjustment_set_page_size(control.fHAdjust, control.width.float)
1130 | gtk_widget_set_size_request(control.fHScrollbar, size.cint, 0)
1131 | gtk_layout_move(control.fHandle, control.fHScrollbar, 0, (control.height - fScrollbarSize).cint)
1132 | gtk_widget_show(control.fHScrollbar)
1133 | # Ensure that scroll pos is within range:
1134 | control.fXScrollPos = max(min(control.fXScrollPos, maxValue), 0)
1135 | else:
1136 | gtk_widget_hide(control.fHScrollbar)
1137 | control.fXScrollPos = 0
1138 |
1139 | # Update vertical scrollbar:
1140 | if control.fYScrollEnabled:
1141 | var upper = control.scrollableHeight
1142 | var size = control.height
1143 | if control.fXScrollEnabled:
1144 | upper.inc(fScrollbarSize)
1145 | size.dec(fScrollbarSize)
1146 | gtk_adjustment_set_upper(control.fVAdjust, upper.cdouble)
1147 | let value = gtk_adjustment_get_value(control.fVAdjust).int
1148 | let maxValue = upper - control.height
1149 | if value > maxValue:
1150 | gtk_adjustment_set_value(control.fVAdjust, maxValue.float)
1151 | gtk_adjustment_set_page_size(control.fVAdjust, control.height.float)
1152 | gtk_widget_set_size_request(control.fVScrollbar, 0, size.cint)
1153 | gtk_layout_move(control.fHandle, control.fVScrollbar, (control.width - fScrollbarSize).cint, 0)
1154 | gtk_widget_show(control.fVScrollbar)
1155 | # Ensure that scroll pos is within range:
1156 | control.fYScrollPos = max(min(control.fYScrollPos, maxValue), 0)
1157 | else:
1158 | gtk_widget_hide(control.fVScrollbar)
1159 | control.fYScrollPos = 0
1160 |
1161 | # Update dead corner:
1162 | if control.fXScrollEnabled and control.fYScrollEnabled:
1163 | gtk_layout_move(control.fHandle, control.fDeadCornerHandle, (control.width - fScrollbarSize).cint, (control.height - fScrollbarSize).cint)
1164 | gtk_widget_show(control.fDeadCornerHandle)
1165 | else:
1166 | gtk_widget_hide(control.fDeadCornerHandle)
1167 |
1168 |
1169 | method setSize(control: ControlImpl, width, height: int) =
1170 | if width == control.fWidth and height == control.fHeight:
1171 | return
1172 | procCall control.Control.setSize(width, height)
1173 | gtk_widget_set_size_request(control.fHandle, width.cint, height.cint)
1174 | pUpdateScrollBar(control)
1175 |
1176 | method setPosition(control: ControlImpl, x, y: int) =
1177 | procCall control.Control.setPosition(x, y)
1178 | if control.fParentControl != nil:
1179 | gtk_layout_move(cast[ContainerImpl](control.fParentControl).fInnerHandle, control.fHandle, x.cint, y.cint)
1180 |
1181 | method forceRedraw(control: ControlImpl) = gtk_widget_queue_draw(control.fHandle)
1182 |
1183 | # proc removeWidgetInternal(container: WidgetContainer, widget: Widget) = gtk_container_remove(container.innerHandle, widget.handle)
1184 |
1185 | method setFontFamily(control: ControlImpl, fontFamily: string) =
1186 | procCall control.Control.setFontFamily(fontFamily)
1187 | control.pUpdateFont()
1188 |
1189 | method setFontSize(control: ControlImpl, fontSize: float) =
1190 | procCall control.Control.setFontSize(fontSize)
1191 | control.pUpdateFont()
1192 |
1193 | method setFontBold(control: ControlImpl, fontBold: bool) =
1194 | procCall control.Control.setFontBold(fontBold)
1195 | control.pUpdateFont()
1196 |
1197 | method setTextColor(control: ControlImpl, color: Color) =
1198 | procCall control.Control.setTextColor(color)
1199 | var rgba: GdkRGBA
1200 | color.pColorToGdkRGBA(rgba)
1201 | gtk_widget_override_color(control.fHandle, GTK_STATE_FLAG_NORMAL, rgba)
1202 |
1203 | method setBackgroundColor(control: ControlImpl, color: Color) =
1204 | procCall control.Control.setBackgroundColor(color)
1205 | var rgba: GdkRGBA
1206 | color.pColorToGdkRGBA(rgba)
1207 | gtk_widget_override_background_color(control.fHandle, GTK_STATE_FLAG_NORMAL, rgba)
1208 |
1209 | method getTextLineWidth(control: ControlImpl, text: string): int =
1210 | var layout = gtk_widget_create_pango_layout(control.fHandle, text)
1211 | var font = pCreateFont(control.fontFamily, control.fontSize, control.fontBold)
1212 | pango_layout_set_font_description(layout, font)
1213 | var width: cint
1214 | var height: cint
1215 | pango_layout_get_pixel_size(layout, width, height)
1216 | result = width + 2
1217 | g_object_unref(layout)
1218 | pango_font_description_free(font)
1219 |
1220 | method getTextLineHeight(control: ControlImpl): int =
1221 | var layout = gtk_widget_create_pango_layout(control.fHandle, "a")
1222 | var font = pCreateFont(control.fontFamily, control.fontSize, control.fontBold)
1223 | pango_layout_set_font_description(layout, font)
1224 | var width: cint
1225 | var height: cint
1226 | pango_layout_get_pixel_size(layout, width, height)
1227 | result = height
1228 | g_object_unref(layout)
1229 | pango_font_description_free(font)
1230 |
1231 | discard """ method `xScrollPos=`(control: ControlImpl, xScrollPos: int) =
1232 | procCall control.Control.`xScrollPos=`(xScrollPos)
1233 | control.pUpdateScrollBar()
1234 |
1235 | method `yScrollPos=`(control: ControlImpl, yScrollPos: int) =
1236 | procCall control.Control.`yScrollPos=`(yScrollPos)
1237 | control.pUpdateScrollBar() """
1238 |
1239 | method `scrollableWidth=`(control: ControlImpl, scrollableWidth: int) =
1240 | if scrollableWidth == control.fScrollableWidth:
1241 | return
1242 | procCall control.Control.`scrollableWidth=`(scrollableWidth)
1243 | control.pUpdateScrollBar()
1244 |
1245 | method `scrollableHeight=`(control: ControlImpl, scrollableHeight: int) =
1246 | if scrollableHeight == control.fScrollableHeight:
1247 | return
1248 | procCall control.Control.`scrollableHeight=`(scrollableHeight)
1249 | control.pUpdateScrollBar()
1250 |
1251 | method mousePosition(control: Control): tuple[x, y: int] =
1252 | var x, y: cint
1253 | gtk_widget_get_pointer(cast[ControlImpl](control).fHandle, x, y)
1254 | result.x = x
1255 | result.y = y
1256 |
1257 |
1258 | # ----------------------------------------------------------------------------------------
1259 | # Container
1260 | # ----------------------------------------------------------------------------------------
1261 |
1262 | proc init(container: ContainerImpl) =
1263 | container.fHandle = gtk_fixed_new()
1264 | # ScrollWnd:
1265 | container.fScrollWndHandle = gtk_scrolled_window_new(nil, nil)
1266 | gtk_widget_show(container.fScrollWndHandle)
1267 | gtk_container_add(container.fHandle, container.fScrollWndHandle)
1268 | # Inner:
1269 | container.fInnerHandle = gtk_layout_new(nil, nil)
1270 | gtk_widget_show(container.fInnerHandle)
1271 | gtk_container_add(container.fScrollWndHandle, container.fInnerHandle)
1272 | container.Container.init()
1273 |
1274 | discard g_signal_connect_data(container.fInnerHandle, "draw", cast[pointer](pControlDrawSignal), cast[pointer](container))
1275 |
1276 | method pAddButtonPressEvent(container: ContainerImpl) =
1277 | # Overwrite base method
1278 | gtk_widget_add_events(container.fInnerHandle, GDK_BUTTON_PRESS_MASK)
1279 | discard g_signal_connect_data(container.fInnerHandle, "button-press-event", cast[pointer](pCustomControlButtonPressSignal), cast[pointer](container))
1280 |
1281 | method pUpdateScrollWnd(container: ContainerImpl) {.base.} =
1282 | let padding = container.getPadding()
1283 | let width = container.width - padding.left - padding.right
1284 | let height = container.height - padding.top - padding.bottom
1285 | gtk_widget_set_size_request(container.fScrollWndHandle, width.cint, height.cint)
1286 | gtk_fixed_move(container.fHandle, container.fScrollWndHandle, padding.left.cint, padding.top.cint)
1287 |
1288 | method `frame=`(container: ContainerImpl, frame: Frame) =
1289 | procCall container.Container.`frame=`(frame)
1290 | if frame != nil:
1291 | gtk_container_add(container.fHandle, frame.fHandle)
1292 | container.pUpdateScrollWnd()
1293 |
1294 | method add(container: ContainerImpl, control: Control) =
1295 | # Overwrite base method
1296 | gtk_container_add(container.fInnerHandle, cast[ControlImpl](control).fHandle)
1297 | procCall container.Container.add(control)
1298 |
1299 | method remove(container: ContainerImpl, control: Control) =
1300 | discard g_object_ref(cast[ControlImpl](control).fHandle) # avoid that the widget is destroyed
1301 | gtk_container_remove(container.fInnerHandle, cast[ControlImpl](control).fHandle)
1302 | procCall container.Container.remove(control)
1303 |
1304 | method paddingLeft(container: ContainerImpl): int {.base.} = 5 # TODO
1305 | method paddingRight(container: ContainerImpl): int {.base.} = 5 # TODO
1306 | method paddingTop(container: ContainerImpl): int {.base.} = 15 # TODO
1307 | method paddingBottom(container: ContainerImpl): int {.base.} = 5 # TODO
1308 |
1309 | method setInnerSize(container: ContainerImpl, width, height: int) =
1310 | procCall container.Container.setInnerSize(width, height)
1311 | gtk_widget_set_size_request(container.fInnerHandle, width.cint, height.cint)
1312 |
1313 | method setSize(container: ContainerImpl, width, height: int) =
1314 | procCall container.Container.setSize(width, height)
1315 | container.pUpdateScrollWnd()
1316 |
1317 | method pUpdateScrollBar(container: ContainerImpl) =
1318 | # Overwrite base method
1319 | if container.fScrollableWidth == -1 or container.fScrollableHeight == -1:
1320 | return
1321 |
1322 | if fScrollbarSize == -1:
1323 | return
1324 |
1325 | gtk_layout_set_size(container.fInnerHandle, container.scrollableWidth.cint, container.scrollableHeight.cint)
1326 |
1327 | container.fXScrollEnabled = false
1328 | container.fYScrollEnabled = false
1329 |
1330 | if container.scrollableWidth > container.width:
1331 | container.fXScrollEnabled = true
1332 | if container.scrollableHeight > container.height:
1333 | container.fYScrollEnabled = true
1334 |
1335 | if container.fXScrollEnabled and not container.fYScrollEnabled and container.scrollableHeight > container.height - fScrollbarSize:
1336 | container.fYScrollEnabled = true
1337 | if container.fYScrollEnabled and not container.fXScrollEnabled and container.scrollableWidth > container.width - fScrollbarSize:
1338 | container.fXScrollEnabled = true
1339 |
1340 | method handleDrawEvent(container: ContainerImpl, event: DrawEvent) =
1341 | # Overwrites base method
1342 | let callback = container.onDraw
1343 | if callback != nil:
1344 | callback(event)
1345 | else:
1346 | # Draw regular window background
1347 | container.canvas.areaColor = app.defaultBackgroundColor
1348 | container.canvas.fill()
1349 |
1350 |
1351 | # ----------------------------------------------------------------------------------------
1352 | # Frame
1353 | # ----------------------------------------------------------------------------------------
1354 |
1355 | proc init(frame: NativeFrame) =
1356 | frame.fHandle = gtk_frame_new("")
1357 | frame.Frame.init()
1358 |
1359 | method `text=`(frame: NativeFrame, text: string) =
1360 | procCall frame.Frame.`text=`(text)
1361 | gtk_frame_set_label(frame.fHandle, text)
1362 |
1363 | method getPadding(frame: NativeFrame): Spacing =
1364 | result = procCall frame.Frame.getPadding()
1365 | result.top = frame.getTextLineHeight() * frame.text.countLines + 2
1366 |
1367 |
1368 | # ----------------------------------------------------------------------------------------
1369 | # Button
1370 | # ----------------------------------------------------------------------------------------
1371 |
1372 | proc init(button: NativeButton) =
1373 | button.fHandle = gtk_button_new()
1374 | button.Button.init()
1375 |
1376 | method `text=`(button: NativeButton, text: string) =
1377 | procCall button.Button.`text=`(text)
1378 | gtk_button_set_label(button.fHandle, text)
1379 | # Don't let the button expand:
1380 | let list = gtk_container_get_children(button.fHandle)
1381 | if list != nil:
1382 | gtk_label_set_ellipsize(list.data, PANGO_ELLIPSIZE_END)
1383 | app.processEvents()
1384 |
1385 | method naturalWidth(button: NativeButton): int =
1386 | # Override parent method, to make it big enough for the text to fit in.
1387 | var context = gtk_widget_get_style_context(button.fHandle)
1388 | var padding: GtkBorder
1389 | gtk_style_context_get_padding(context, GTK_STATE_FLAG_NORMAL, padding)
1390 | result = button.getTextLineWidth(button.text) + padding.left + padding.right + 5
1391 |
1392 | method `enabled=`(button: NativeButton, enabled: bool) =
1393 | button.fEnabled = enabled
1394 | gtk_widget_set_sensitive(button.fHandle, enabled)
1395 |
1396 |
1397 | # ----------------------------------------------------------------------------------------
1398 | # Checkbox
1399 | # ----------------------------------------------------------------------------------------
1400 |
1401 | proc pControlToggledSignal(widget: pointer, data: pointer): Gboolean {.cdecl.} =
1402 | let control = cast[Checkbox](data)
1403 | var evt = new ToggleEvent
1404 | evt.control = control
1405 | try:
1406 | control.handleToggleEvent(evt)
1407 | except:
1408 | handleException()
1409 |
1410 | proc init(checkbox: NativeCheckbox) =
1411 | checkbox.fHandle = gtk_check_button_new()
1412 | discard g_signal_connect_data(checkbox.fHandle, "toggled", cast[pointer](pControlToggledSignal), cast[pointer](checkbox))
1413 | checkbox.Checkbox.init()
1414 | gtk_widget_show(checkbox.fHandle)
1415 |
1416 | method `text=`(checkbox: NativeCheckbox, text: string) =
1417 | procCall checkbox.Checkbox.`text=`(text)
1418 | gtk_button_set_label(checkbox.fHandle, text)
1419 | app.processEvents()
1420 |
1421 | method naturalWidth(checkbox: NativeCheckbox): int =
1422 | # Override parent method, to make it big enough for the text to fit in.
1423 | var context = gtk_widget_get_style_context(checkbox.fHandle)
1424 | var padding: GtkBorder
1425 | gtk_style_context_get_padding(context, GTK_STATE_FLAG_NORMAL, padding)
1426 | result = checkbox.getTextLineWidth(checkbox.text) + padding.left + padding.right + 25.scaleToDpi
1427 |
1428 | method `enabled=`(checkbox: NativeCheckbox, enabled: bool) =
1429 | checkbox.fEnabled = enabled
1430 | gtk_widget_set_sensitive(checkbox.fHandle, enabled)
1431 |
1432 | method checked(checkbox: NativeCheckbox): bool =
1433 | result = gtk_toggle_button_get_active(checkbox.fHandle)
1434 |
1435 | method `checked=`(checkbox: NativeCheckbox, checked: bool) =
1436 | g_signal_handlers_block_matched(checkbox.fHandle, G_SIGNAL_MATCH_FUNC, 0, nil, nil, cast[pointer](pControlToggledSignal))
1437 | gtk_toggle_button_set_active(checkbox.fHandle, checked)
1438 | g_signal_handlers_unblock_matched(checkbox.fHandle, G_SIGNAL_MATCH_FUNC, 0, nil, nil, cast[pointer](pControlToggledSignal))
1439 |
1440 | method pAddButtonPressEvent(checkbox: NativeCheckbox) = discard # don't override default handler
1441 |
1442 |
1443 | # ----------------------------------------------------------------------------------------
1444 | # ComboBox
1445 | # ----------------------------------------------------------------------------------------
1446 |
1447 | proc pComboBoxChangedSignal(widget: pointer, data: pointer): Gboolean {.cdecl.} =
1448 | let control = cast[ComboBox](data)
1449 | var evt = new ComboBoxChangeEvent
1450 | evt.control = control
1451 | try:
1452 | control.handleChangeEvent(evt)
1453 | except:
1454 | handleException()
1455 |
1456 | proc init(comboBox: NativeComboBox) =
1457 | comboBox.fHandle = gtk_combo_box_text_new()
1458 | discard g_signal_connect_data(comboBox.fHandle, "changed", cast[pointer](pComboBoxChangedSignal), cast[pointer](comboBox))
1459 | comboBox.ComboBox.init()
1460 | gtk_widget_show(comboBox.fHandle)
1461 |
1462 | method naturalWidth(comboBox: NativeComboBox): int =
1463 | result = 0
1464 | for option in comboBox.options:
1465 | result = max(result, comboBox.getTextWidth(option) + 20.scaleToDpi())
1466 |
1467 | method naturalHeight(comboBox: NativeComboBox): int =
1468 | result = comboBox.getTextLineHeight() + 12.scaleToDpi()
1469 |
1470 | method `options=`(comboBox: NativeComboBox, options: seq[string]) =
1471 | let oldIndex = comboBox.index
1472 | comboBox.fOptions = options
1473 |
1474 | gtk_combo_box_text_remove_all(comboBox.fHandle)
1475 | for option in options:
1476 | gtk_combo_box_text_append_text(comboBox.fHandle, option.cstring)
1477 |
1478 | comboBox.triggerRelayout()
1479 |
1480 | if oldIndex < len(options):
1481 | comboBox.index = oldIndex
1482 | else:
1483 | comboBox.index = len(options) - 1
1484 |
1485 | method `enabled=`(comboBox: NativeComboBox, enabled: bool) =
1486 | comboBox.fEnabled = enabled
1487 | gtk_widget_set_sensitive(comboBox.fHandle, enabled)
1488 |
1489 | method value(comboBox: NativeComboBox): string =
1490 | result = $gtk_combo_box_text_get_active_text(comboBox.fHandle)
1491 |
1492 | method `value=`(comboBox: NativeComboBox, value: string) =
1493 | let idx = comboBox.fOptions.find(value)
1494 | if idx != -1:
1495 | comboBox.index = idx
1496 |
1497 | method index(comboBox: NativeComboBox): int =
1498 | result = gtk_combo_box_get_active(comboBox.fHandle)
1499 |
1500 | method `index=`(comboBox: NativeComboBox, index: int) =
1501 | gtk_combo_box_set_active(comboBox.fHandle, index.cint)
1502 |
1503 |
1504 | # ----------------------------------------------------------------------------------------
1505 | # Label
1506 | # ----------------------------------------------------------------------------------------
1507 |
1508 | proc init(label: NativeLabel) =
1509 | label.Label.init()
1510 | label.fFontSize = app.defaultFontSize * 0.95
1511 |
1512 |
1513 | # ----------------------------------------------------------------------------------------
1514 | # ProgressBar
1515 | # ----------------------------------------------------------------------------------------
1516 |
1517 | proc init(progressBar: NativeProgressBar) =
1518 | progressBar.fHandle = gtk_level_bar_new()
1519 | progressBar.ProgressBar.init()
1520 | progressBar.height = 8.scaleToDpi # adjust control height to bar height
1521 |
1522 | method `value=`(progressBar: NativeProgressBar, value: float) =
1523 | procCall progressBar.ProgressBar.`value=`(value)
1524 | gtk_level_bar_set_value(progressBar.fHandle, value)
1525 | app.processEvents()
1526 |
1527 |
1528 | # ----------------------------------------------------------------------------------------
1529 | # TextBox
1530 | # ----------------------------------------------------------------------------------------
1531 |
1532 | proc pTextBoxKeyPressSignal(widget: pointer, event: var GdkEventKey, data: pointer): Gboolean {.cdecl.} =
1533 | result = pControlKeyPressSignal(widget, event, data)
1534 |
1535 | # Implement own "copy to clipboard", because by default the clipboard is non-persistent
1536 | if not result:
1537 | let modifiers = gtk_accelerator_get_default_mod_mask()
1538 | if event.keyval == 'c'.ord and (event.state and modifiers) == GDK_CONTROL_MASK:
1539 | let textBox = cast[NativeTextBox](data)
1540 | app.clipboardText = textBox.selectedText
1541 | return true # prevent default "copy to clipboard"
1542 |
1543 | proc init(textBox: NativeTextBox) =
1544 | textBox.fHandle = gtk_entry_new()
1545 | discard g_signal_connect_data(textBox.fHandle, "changed", cast[pointer](pControlChangedSignal), cast[pointer](textBox))
1546 | textBox.TextBox.init()
1547 |
1548 | method initStyle(textBox: NativeTextBox) =
1549 | procCall textBox.TextBox.initStyle()
1550 | var context = gtk_widget_get_style_context(textBox.fHandle)
1551 | var rgba: GdkRGBA
1552 | gtk_style_context_get_background_color(context, GTK_STATE_FLAG_NORMAL, rgba)
1553 | textBox.fBackgroundColor = rgba.pGdkRGBAToColor()
1554 | gtk_style_context_get_color(context, GTK_STATE_FLAG_NORMAL, rgba)
1555 | textBox.fTextColor = rgba.pGdkRGBAToColor()
1556 | textBox.fUseDefaultBackgroundColor = false
1557 | textBox.fUseDefaultTextColor = false
1558 |
1559 | method text(textBox: NativeTextBox): string = $gtk_entry_get_text(textBox.fHandle)
1560 |
1561 | method `text=`(textBox: NativeTextBox, text: string) =
1562 | gtk_entry_set_text(textBox.fHandle, text)
1563 | app.processEvents()
1564 |
1565 | method `placeholder=`(textBox: NativeTextBox, text: string) =
1566 | gtk_entry_set_placeholder_text(textBox.fHandle, text)
1567 |
1568 | method naturalHeight(textBox: NativeTextBox): int = textBox.getTextLineHeight() + 12 # add padding
1569 |
1570 | method setSize(textBox: NativeTextBox, width, height: int) =
1571 | gtk_entry_set_width_chars(textBox.fHandle, 1)
1572 | procCall textBox.ControlImpl.setSize(width, height)
1573 |
1574 | method pAddButtonPressEvent(textBox: NativeTextBox) =
1575 | gtk_widget_add_events(textBox.fHandle, GDK_BUTTON_PRESS_MASK)
1576 | discard g_signal_connect_data(textBox.fHandle, "button-press-event", cast[pointer](pDefaultControlButtonPressSignal), cast[pointer](textBox))
1577 |
1578 | method pAddKeyPressEvent(textBox: NativeTextBox) =
1579 | discard g_signal_connect_data(textBox.fHandle, "key-press-event", cast[pointer](pTextBoxKeyPressSignal), cast[pointer](textBox))
1580 |
1581 | method `editable=`(textBox: NativeTextBox, editable: bool) =
1582 | textBox.fEditable = editable
1583 | gtk_editable_set_editable(textBox.fHandle, editable)
1584 |
1585 | method cursorPos(textBox: NativeTextBox): int =
1586 | result = gtk_editable_get_position(textBox.fHandle)
1587 |
1588 | method `cursorPos=`(textBox: NativeTextBox, cursorPos: int) =
1589 | # side effect: clears selection
1590 | gtk_editable_set_position(textBox.fHandle, cursorPos.cint)
1591 |
1592 | method selectionStart(textBox: NativeTextBox): int =
1593 | var startPos: cint
1594 | var endPos: cint
1595 | discard gtk_editable_get_selection_bounds(textBox.fHandle, startPos, endPos)
1596 | result = startPos
1597 |
1598 | method selectionEnd(textBox: NativeTextBox): int =
1599 | var startPos: cint
1600 | var endPos: cint
1601 | discard gtk_editable_get_selection_bounds(textBox.fHandle, startPos, endPos)
1602 | result = endPos
1603 |
1604 | method `selectionStart=`(textBox: NativeTextBox, selectionStart: int) =
1605 | gtk_editable_select_region(textBox.fHandle, selectionStart.cint, textBox.selectionEnd.cint)
1606 | # side effect: sets cursor to end of selection
1607 |
1608 | method `selectionEnd=`(textBox: NativeTextBox, selectionEnd: int) =
1609 | gtk_editable_select_region(textBox.fHandle, textBox.selectionStart.cint, selectionEnd.cint)
1610 | # side effect: sets cursor to end of selection
1611 |
1612 |
1613 | # ----------------------------------------------------------------------------------------
1614 | # TextArea
1615 | # ----------------------------------------------------------------------------------------
1616 |
1617 | proc pTextAreaKeyPressSignal(widget: pointer, event: var GdkEventKey, data: pointer): Gboolean {.cdecl.} =
1618 | result = pControlKeyPressSignal(widget, event, data)
1619 |
1620 | # Implement own "copy to clipboard", because by default the clipboard is non-persistent
1621 | if not result:
1622 | let modifiers = gtk_accelerator_get_default_mod_mask()
1623 | if event.keyval == 'c'.ord and (event.state and modifiers) == GDK_CONTROL_MASK:
1624 | let textArea = cast[NativeTextBox](data)
1625 | app.clipboardText = textArea.selectedText
1626 | return true # prevent default "copy to clipboard"
1627 |
1628 | proc init(textArea: NativeTextArea) =
1629 | textArea.fHandle = gtk_scrolled_window_new(nil, nil)
1630 | # gtk_scrolled_window_set_policy(textArea.fHandle, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC)
1631 | gtk_scrolled_window_set_policy(textArea.fHandle, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC)
1632 | textArea.fTextViewHandle = gtk_text_view_new()
1633 | gtk_text_view_set_left_margin(textArea.fTextViewHandle, 5)
1634 | gtk_text_view_set_right_margin(textArea.fTextViewHandle, 5)
1635 | gtk_text_view_set_top_margin(textArea.fTextViewHandle, 5)
1636 | gtk_text_view_set_bottom_margin(textArea.fTextViewHandle, 5)
1637 | gtk_container_add(textArea.fHandle, textArea.fTextViewHandle)
1638 | gtk_widget_show(textArea.fTextViewHandle)
1639 | textArea.fBufferHandle = gtk_text_view_get_buffer(textArea.fTextViewHandle)
1640 | discard g_signal_connect_data(textArea.fBufferHandle, "changed", cast[pointer](pControlChangedSignal), cast[pointer](textArea))
1641 | textArea.TextArea.init()
1642 |
1643 | method setSize(textBox: NativeTextArea, width, height: int) =
1644 | # Need to override method of NativeTextBox
1645 | procCall textBox.ControlImpl.setSize(width, height)
1646 |
1647 | method text(textArea: NativeTextArea): string =
1648 | var startIter, endIter: GtkTextIter
1649 | gtk_text_buffer_get_start_iter(textArea.fBufferHandle, startIter)
1650 | gtk_text_buffer_get_end_iter(textArea.fBufferHandle, endIter)
1651 | result = $gtk_text_buffer_get_text(textArea.fBufferHandle, startIter, endIter, true)
1652 |
1653 | method `text=`(textArea: NativeTextArea, text: string) =
1654 | gtk_text_buffer_set_text(textArea.fBufferHandle, text, text.len.cint)
1655 | app.processEvents()
1656 |
1657 | method addText(textArea: NativeTextArea, text: string) =
1658 | # overide base method for better performance and to prevent automatic scrolling to top
1659 | var iter: GtkTextIter
1660 | gtk_text_buffer_get_end_iter(textArea.fBufferHandle, iter)
1661 | gtk_text_buffer_insert(textArea.fBufferHandle, iter, text, text.len.cint)
1662 | app.processEvents()
1663 |
1664 | method addColoredText(textArea: NativeTextArea, text, color: string) =
1665 | if color == "":
1666 | textArea.addText(text)
1667 | else:
1668 | if not textArea.fColorTags.contains(color):
1669 | discard gtk_text_buffer_create_tag(textArea.fBufferHandle, color, "foreground", color, nil)
1670 | textArea.fColorTags.add(color)
1671 | var iter: GtkTextIter
1672 | gtk_text_buffer_get_end_iter(textArea.fBufferHandle, iter)
1673 | gtk_text_buffer_insert_with_tags_by_name(textArea.fBufferHandle, iter, text, text.len.cint, color, nil)
1674 | app.processEvents()
1675 |
1676 | method scrollToBottom(textArea: NativeTextArea) =
1677 | var iter: GtkTextIter
1678 | gtk_text_buffer_get_end_iter(textArea.fBufferHandle, iter)
1679 | gtk_text_view_scroll_to_iter(textArea.fTextViewHandle, iter, 0, false, 0, 0)
1680 | app.processEvents()
1681 |
1682 | method `wrap=`(textArea: NativeTextArea, wrap: bool) =
1683 | procCall textArea.TextArea.`wrap=`(wrap)
1684 | if wrap:
1685 | gtk_text_view_set_wrap_mode(textArea.fTextViewHandle, GTK_WRAP_WORD_CHAR)
1686 | else:
1687 | gtk_text_view_set_wrap_mode(textArea.fTextViewHandle, GTK_WRAP_NONE)
1688 |
1689 | method pAddKeyPressEvent(textArea: NativeTextArea) =
1690 | discard g_signal_connect_data(textArea.fTextViewHandle, "key-press-event", cast[pointer](pTextAreaKeyPressSignal), cast[pointer](textArea))
1691 |
1692 | method `editable=`(textArea: NativeTextArea, editable: bool) =
1693 | textArea.fEditable = editable
1694 | gtk_text_view_set_editable(textArea.fTextViewHandle, editable)
1695 |
1696 | method cursorPos(textArea: NativeTextArea): int =
1697 | let mark = gtk_text_buffer_get_insert(textArea.fBufferHandle)
1698 | var iter: GtkTextIter
1699 | gtk_text_buffer_get_iter_at_mark(textArea.fBufferHandle, iter, mark)
1700 | result = gtk_text_iter_get_offset(iter)
1701 |
1702 | method `cursorPos=`(textArea: NativeTextArea, cursorPos: int) =
1703 | # side effect: clears selection
1704 | var iter: GtkTextIter
1705 | gtk_text_buffer_get_iter_at_offset(textArea.fBufferHandle, iter, cursorPos.cint)
1706 | gtk_text_buffer_select_range(textArea.fBufferHandle, iter, iter)
1707 |
1708 | method selectionStart(textArea: NativeTextArea): int =
1709 | var startIter: GtkTextIter
1710 | var endIter: GtkTextIter
1711 | discard gtk_text_buffer_get_selection_bounds(textArea.fBufferHandle, startIter, endIter)
1712 | result = gtk_text_iter_get_offset(startIter)
1713 |
1714 | method selectionEnd(textArea: NativeTextArea): int =
1715 | var startIter: GtkTextIter
1716 | var endIter: GtkTextIter
1717 | discard gtk_text_buffer_get_selection_bounds(textArea.fBufferHandle, startIter, endIter)
1718 | result = gtk_text_iter_get_offset(endIter)
1719 |
1720 | method `selectionStart=`(textArea: NativeTextArea, selectionStart: int) =
1721 | var startIter: GtkTextIter
1722 | var endIter: GtkTextIter
1723 | gtk_text_buffer_get_iter_at_offset(textArea.fBufferHandle, startIter, selectionStart.cint)
1724 | gtk_text_buffer_get_iter_at_offset(textArea.fBufferHandle, endIter, textArea.selectionEnd.cint)
1725 | gtk_text_buffer_select_range(textArea.fBufferHandle, startIter, endIter)
1726 | # side effect: sets cursor to start of selection
1727 |
1728 | method `selectionEnd=`(textArea: NativeTextArea, selectionEnd: int) =
1729 | var startIter: GtkTextIter
1730 | var endIter: GtkTextIter
1731 | gtk_text_buffer_get_iter_at_offset(textArea.fBufferHandle, startIter, textArea.selectionStart.cint)
1732 | gtk_text_buffer_get_iter_at_offset(textArea.fBufferHandle, endIter, selectionEnd.cint)
1733 | gtk_text_buffer_select_range(textArea.fBufferHandle, startIter, endIter)
1734 | # side effect: sets cursor to start of selection
1735 |
--------------------------------------------------------------------------------
/src/nigui/private/gtk3/platform_types1.nim:
--------------------------------------------------------------------------------
1 | # NiGui - GTK+ 3 platform-specific code - part 1
2 |
3 | # This file will be included in "nigui.nim".
4 |
5 | type
6 | WindowImpl* = ref object of Window
7 | fHandle: pointer
8 | fInnerHandle: pointer
9 | fIMContext: pointer
10 | fKeyPressed: Key
11 |
12 | ControlImpl* = ref object of Control
13 | fHandle: pointer
14 | fHScrollbar: pointer
15 | fVScrollbar: pointer
16 | fHAdjust: pointer
17 | fVAdjust: pointer
18 | fDeadCornerHandle: pointer
19 | fIMContext: pointer
20 | fKeyPressed: Key
21 |
22 | CanvasImpl* = ref object of Canvas
23 | fSurface: pointer
24 | fData: ptr UncheckedArray[byte]
25 | fStride: int
26 | fCairoContext: pointer
27 | fFont: pointer
28 |
29 | ImageImpl* = ref object of Image
30 |
--------------------------------------------------------------------------------
/src/nigui/private/gtk3/platform_types2.nim:
--------------------------------------------------------------------------------
1 | # NiGui - GTK+ 3 platform-specific code - part 2
2 |
3 | # This file will be included in "nigui.nim".
4 |
5 | type
6 | ContainerImpl* = ref object of Container
7 | fScrollWndHandle: pointer
8 | fInnerHandle: pointer
9 |
10 | NativeFrame* = ref object of Frame
11 |
12 | NativeButton* = ref object of Button
13 |
14 | NativeCheckbox* = ref object of Checkbox
15 |
16 | NativeComboBox* = ref object of ComboBox
17 |
18 | NativeLabel* = ref object of Label
19 |
20 | NativeProgressBar* = ref object of ProgressBar
21 |
22 | NativeTextBox* = ref object of TextBox
23 |
--------------------------------------------------------------------------------
/src/nigui/private/gtk3/platform_types3.nim:
--------------------------------------------------------------------------------
1 | # NiGui - GTK+ 3 platform-specific code - part 3
2 |
3 | # This file will be included in "nigui.nim".
4 |
5 | type
6 | NativeTextArea* = ref object of TextArea
7 | fTextViewHandle: pointer
8 | fBufferHandle: pointer
9 | fColorTags: seq[string]
10 |
--------------------------------------------------------------------------------
/src/nigui/private/windows/platform_types1.nim:
--------------------------------------------------------------------------------
1 | import windows
2 |
3 | # NiGui - Win32 platform-specific code - part 1
4 |
5 | # This file will be included in "nigui.nim".
6 |
7 | type
8 | WindowImpl* = ref object of Window
9 | fHandle: pointer
10 | fModalParent: WindowImpl
11 | fFocusedControl: ControlImpl
12 |
13 | ControlImpl* = ref object of Control
14 | fHandle: pointer
15 | fFont: pointer
16 | fBackgroundBrush: pointer
17 |
18 | CanvasImpl* = ref object of Canvas
19 | fDC: pointer
20 | fBitmap: pointer
21 | fGraphics: pointer
22 | fFont: pointer
23 | fFontBrush: pointer
24 | fLinePen: pointer
25 | fAreaBrush: pointer
26 |
27 | ImageImpl* = ref object of Image
28 | bitmapDataLockBits: BitmapData
29 |
--------------------------------------------------------------------------------
/src/nigui/private/windows/platform_types2.nim:
--------------------------------------------------------------------------------
1 | # NiGui - Win32 platform-specific code - part 2
2 |
3 | # This file will be included in "nigui.nim".
4 |
5 | type
6 | ContainerImpl* = ref object of Container
7 | fScrollWndHandle: pointer
8 | fInnerHandle: pointer
9 |
10 | NativeFrame* = ref object of Frame
11 |
12 | NativeButton* = ref object of Button
13 |
14 | NativeCheckbox* = ref object of Checkbox
15 |
16 | NativeComboBox* = ref object of ComboBox
17 |
18 | NativeLabel* = ref object of Label
19 |
20 | NativeProgressBar* = ref object of ProgressBar
21 |
22 | NativeTextBox* = ref object of TextBox
23 |
--------------------------------------------------------------------------------
/src/nigui/private/windows/platform_types3.nim:
--------------------------------------------------------------------------------
1 | # NiGui - Win32 platform-specific code - part 3
2 |
3 | # This file will be included in "nigui.nim".
4 |
5 | type
6 | NativeTextArea* = ref object of TextArea
7 |
--------------------------------------------------------------------------------
/src/nigui/private/windows/res/amd64.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simonkrauter/NiGui/f8167a82517973d7cd47797801f90b06360e3f32/src/nigui/private/windows/res/amd64.o
--------------------------------------------------------------------------------
/src/nigui/private/windows/res/i386.o:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simonkrauter/NiGui/f8167a82517973d7cd47797801f90b06360e3f32/src/nigui/private/windows/res/i386.o
--------------------------------------------------------------------------------
/src/nigui/private/windows/res/manifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
52 |
59 |
60 |
61 |
62 |
63 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/src/nigui/private/windows/res/resource.rc:
--------------------------------------------------------------------------------
1 | #pragma code_page(65001)
2 |
3 | 1 MANIFEST "manifest.xml"
4 |
--------------------------------------------------------------------------------
/src/nigui/private/windows/windows.nim:
--------------------------------------------------------------------------------
1 | # NiGui - minimal Win32 binding
2 |
3 | # Some functions requires Windows XP or newer.
4 | # Windows type names are replaced with basic types.
5 |
6 | # Type aliases for int16:
7 | # ATOM, SHORT, USHORT, LANGID
8 |
9 | # Type aliases for int32:
10 | # int, UINT, WINUINT, DWORD, LONG, COLORREF
11 |
12 | # Type aliases for int:
13 | # WPARAM, LPARAM, ULONG
14 |
15 | # Type aliases for pointer:
16 | # WNDPROC, HINSTANCE, HICON, HCURSOR, HBRUSH, HWND, LPMSG, LRESULT, PACTCTX, HMODULE, HDC, HGDIOBJ, HFONT, HMONITOR, HGDIOBJ
17 |
18 | # Type aliases for cstring:
19 | # LPCTSTR, LPCWSTR
20 |
21 | {.pragma: libUser32, stdcall, dynlib: "User32.dll".}
22 | {.pragma: libKernel32, stdcall, dynlib: "Kernel32.dll".}
23 | {.pragma: libGdi32, stdcall, dynlib: "Gdi32.dll".}
24 | {.pragma: libShell32, stdcall, dynlib: "Shell32.dll".}
25 | {.pragma: libGdiplus, stdcall, dynlib: "Gdiplus.dll".}
26 | {.pragma: libComdlg32, stdcall, dynlib: "Comdlg32.dll".}
27 |
28 |
29 | # ----------------------------------------------------------------------------------------
30 | # Constants
31 | # ----------------------------------------------------------------------------------------
32 |
33 | const
34 | # ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID* = 4
35 | # ACTCTX_FLAG_RESOURCE_NAME_VALID* = 8
36 | # ACTCTX_FLAG_SET_PROCESS_DEFAULT* = 16
37 | BIF_RETURNONLYFSDIRS* = 0x00000001
38 | BIF_NEWDIALOGSTYLE* = 0x00000040
39 | BN_CLICKED* = 0
40 | BM_SETSTYLE* = 244
41 | BM_SETIMAGE* = 247
42 | BM_GETCHECK* = 240
43 | BM_SETCHECK* = 241
44 | BM_GETSTATE* = 242
45 | BS_DEFPUSHBUTTON* = 0x00000001
46 | BS_AUTOCHECKBOX* = 0x00000003
47 | BS_GROUPBOX* = 0x00000007
48 | BST_UNCHECKED* = 0
49 | BST_CHECKED* = 1
50 | CB_ERR* = -1
51 | CB_ADDSTRING* = 0x0143
52 | CB_DELETESTRING* = 0x0144
53 | CB_GETCOUNT* = 0x0146
54 | CB_GETCURSEL* = 0x0147
55 | CB_SELECTSTRING* = 0x014D
56 | CB_SETCURSEL* = 0x014E
57 | CBS_SIMPLE* = 1
58 | CBS_DROPDOWN* = 2
59 | CBS_DROPDOWNLIST* = 3
60 | CBN_SELCHANGE* = 1
61 | CF_TEXT* = 1
62 | COLOR_BTNFACE* = 15
63 | COLOR_WINDOW* = 5
64 | COLOR_WINDOWTEXT* = 8
65 | CP_UTF8* = 65001
66 | CS_HREDRAW* = 2
67 | CS_VREDRAW* = 1
68 | DEFAULT_GUI_FONT* = 17
69 | EM_SCROLLCARET* = 183
70 | EM_GETSEL* = 176
71 | EM_SETSEL* = 177
72 | EM_REPLACESEL* = 194
73 | EM_SETREADONLY* = 207
74 | EN_CHANGE* = 768
75 | ES_MULTILINE* = 4
76 | ES_AUTOHSCROLL* = 0x80
77 | EM_SETCUEBANNER* = 0x1501
78 | GCLP_HBRBACKGROUND* = -10
79 | GWL_EXSTYLE* = -20
80 | GWL_HINSTANCE* = -6
81 | GWL_HWNDPARENT* = -8
82 | GWL_STYLE* = -16
83 | GWLP_USERDATA* = -21
84 | GWLP_WNDPROC* = -4
85 | HWND_TOPMOST* = -1
86 | HWND_NOTOPMOST* = -2
87 | ICON_SMALL* = 0
88 | ICON_BIG* = 1
89 | IDC_ARROW* = 32512
90 | INVALID_HANDLE_VALUE* = cast[pointer](-1)
91 | InterpolationMode_Default* = 0
92 | InterpolationMode_Bilinear* = 3
93 | InterpolationMode_NearestNeighbor* = 5
94 | IMAGE_BITMAP* = 0
95 | IMAGE_ICON* = 1
96 | # LR_LOADFROMFILE* = 16
97 | OBJ_BRUSH* = 2
98 | PBM_SETPOS* = 1026
99 | PBM_SETRANGE32* = 1030
100 | PM_REMOVE* = 1
101 | SB_HORZ* = 0
102 | SB_THUMBPOSITION* = 4
103 | SB_THUMBTRACK* = 5
104 | SB_VERT* = 1
105 | SB_LINELEFT* = 0
106 | SB_LINERIGHT* = 1
107 | SB_PAGELEFT* = 2
108 | SB_PAGERIGHT* = 3
109 | SC_MINIMIZE* = 0xF020
110 | SC_RESTORE* = 0xF120
111 | SIF_ALL* = 23
112 | SM_CXVSCROLL* = 2
113 | SPI_GETWORKAREA* = 0x0030
114 | SPI_SETKEYBOARDCUES* = 0x100B
115 | # SS_CENTERIMAGE* = 0x00000200
116 | SW_HIDE* = 0
117 | # SW_MAXIMIZE = 3
118 | SW_SHOW* = 5
119 | SW_MINIMIZE* = 6
120 | SW_RESTORE* = 9
121 | SWP_NOMOVE* = 2
122 | SWP_NOSIZE* = 1
123 | VK_RETURN* = 13
124 | VK_SHIFT* = 16
125 | VK_CONTROL* = 17
126 | VK_MENU* = 18
127 | VK_PRIOR* = 33
128 | VK_NEXT* = 34
129 | VK_END* = 35
130 | VK_HOME* = 36
131 | VK_LEFT* = 37
132 | VK_UP* = 38
133 | VK_RIGHT* = 39
134 | VK_DOWN* = 40
135 | VK_SNAPSHOT* = 44
136 | VK_INSERT* = 45
137 | VK_DELETE* = 46
138 | VK_LSHIFT* = 160
139 | VK_RSHIFT* = 161
140 | VK_LCONTROL* = 162
141 | VK_RCONTROL* = 163
142 | VK_LMENU* = 164
143 | VK_RMENU* = 165
144 | VK_OEM_PLUS* = 187
145 | VK_OEM_COMMA* = 188
146 | VK_OEM_MINUS* = 189
147 | VK_OEM_PERIOD* = 190
148 | VK_OEM_2* = 191
149 | VK_OEM_4* = 219
150 | VK_OEM_5* = 220
151 | VK_OEM_102* = 226
152 | WM_USER* = 0x0400
153 | WM_APP* = 0x8000
154 | WM_ACTIVATE* = 0x0006
155 | WM_CHANGEUISTATE* = 0x0127
156 | WM_CHAR* = 258
157 | WM_CLOSE* = 16
158 | WM_COMMAND* = 273
159 | WM_DROPFILES* = 563
160 | WM_ERASEBKGND* = 20
161 | WM_HSCROLL* = 276
162 | WM_KEYDOWN* = 256
163 | WM_KEYUP* = 257
164 | WM_MOUSEMOVE* = 0x0200
165 | WM_LBUTTONDOWN* = 0x0201
166 | WM_LBUTTONUP* = 0x0202
167 | WM_MBUTTONDOWN* = 0x0207
168 | WM_MBUTTONUP* = 0x0208
169 | # WM_NCLBUTTONDOWN* = 161
170 | # WM_NCLBUTTONUP* = 162
171 | WM_MOUSEWHEEL* = 0x020A
172 | WM_MOUSELEAVE* = 0x02A3
173 | WM_MOVE* = 3
174 | WM_NEXTDLGCTL* = 0x0028
175 | WM_PAINT* = 15
176 | WM_RBUTTONDOWN* = 0x0204
177 | WM_RBUTTONUP* = 0x0205
178 | WM_SETFOCUS* = 0x0007
179 | WM_KILLFOCUS* = 0x0008
180 | WM_SETFONT* = 48
181 | WM_SIZE* = 5
182 | WM_VSCROLL* = 277
183 | WM_SETICON* = 128
184 | WM_SYSKEYDOWN* = 260
185 | WM_SYSKEYUP* = 261
186 | WM_SYSCOMMAND* = 274
187 | WM_CTLCOLOREDIT* = 307
188 | WM_CTLCOLORSTATIC* = 312
189 | WM_CUT* = 0x0300
190 | WM_COPY* = 0x0301
191 | WM_PASTE* = 0x0302
192 | WM_GETMINMAXINFO* = 0x0024
193 | WS_CLIPCHILDREN* = 0x02000000
194 | WS_CAPTION* = 0x00C00000
195 | WS_CHILD* = 0x40000000
196 | WS_EX_CLIENTEDGE* = 0x00000200
197 | WS_GROUP* = 0x00020000
198 | WS_HSCROLL* = 0x00100000
199 | WS_OVERLAPPEDWINDOW* = 0x00CF0000
200 | WS_SYSMENU* = 0x00080000
201 | WS_MINIMIZEBOX* = 0x00020000
202 | WS_MAXIMIZEBOX* = 0x00010000
203 | WS_TABSTOP* = 0x00010000
204 | WS_THICKFRAME* = 0x00040000
205 | WS_VSCROLL* = 0x00200000
206 | WS_EX_CONTROLPARENT* = 0x00010000
207 | WS_VISIBLE* = 0x10000000
208 | # DT_CALCRECT* = 1024
209 | # OBJ_FONT* = 6
210 | # SM_XVIRTUALSCREEN* = 76
211 | # WC_LISTVIEWW* = "SysListView32"
212 | # WC_TABCONTROLW* = "SysTabControl32"
213 | # WC_TREEVIEWW* = "SysTreeView32"
214 | # WM_CTLCOLORSTATIC* = 312
215 | # WS_EX_TOOLWINDOW* = 0x00000080
216 | OPAQUE* = 2
217 | TRANSPARENT* = 1
218 | PS_SOLID* = 0
219 | PS_DASH* = 1
220 | PS_DOT* = 2
221 | PS_DASHDOT* = 3
222 | PS_DASHDOTDOT* = 4
223 | PS_NULL* = 5
224 | PS_USERSTYLE* = 7
225 | PS_INSIDEFRAME* = 6
226 | OFN_ALLOWMULTISELECT* = 0x00000200
227 | OFN_EXPLORER* = 0x00080000
228 | OFN_FILEMUSTEXIST* = 0x00001000
229 | OFN_OVERWRITEPROMPT* = 0x00000002
230 | # UnitWorld* = 0
231 | # UnitDisplay* = 1
232 | UnitPixel* = 2
233 | # UnitPoint* = 3
234 | # UnitInch* = 4
235 | # UnitDocument* = 5
236 | # UnitMillimeter* = 6
237 | GMEM_MOVEABLE* = 2
238 | PixelFormat32bppARGB* = 2498570
239 | ImageLockModeWrite* = 2
240 | MAPVK_VSC_TO_VK_EX* = 3
241 | PROCESS_DPI_UNAWARE* = 0
242 | PROCESS_SYSTEM_DPI_AWARE* = 1
243 | PROCESS_PER_MONITOR_DPI_AWARE* = 2
244 | FontStyleRegular* = 0
245 | FontStyleBold* = 1
246 | TextRenderingHint_AntiAlias* = 4
247 | TME_LEAVE* = 0x00000002
248 |
249 |
250 | # ----------------------------------------------------------------------------------------
251 | # Types
252 | # ----------------------------------------------------------------------------------------
253 |
254 | type
255 | WndClassEx* = object
256 | cbSize*: int32
257 | style*: int32
258 | lpfnWndProc*: pointer
259 | cbClsExtra*: int32
260 | cbWndExtra*: int32
261 | hInstance*: pointer
262 | hIcon*: pointer
263 | hCursor*: pointer
264 | hbrBackground*: pointer
265 | lpszMenuName*: cstring
266 | lpszClassName*: cstring
267 | hIconSm*: pointer
268 |
269 | Point* = object
270 | x*: int32
271 | y*: int32
272 |
273 | Size* = object
274 | cx*: int32
275 | cy*: int32
276 |
277 | Rect* = object
278 | left*: int32
279 | top*: int32
280 | right*: int32
281 | bottom*: int32
282 |
283 | RectF* = object
284 | x*: cfloat
285 | y*: cfloat
286 | width*: cfloat
287 | height*: cfloat
288 |
289 | RGB32* = object
290 | red*: byte
291 | green*: byte
292 | blue*: byte
293 | unused: byte
294 |
295 | ARGB* = object
296 | blue*: byte
297 | green*: byte
298 | red*: byte
299 | alpha*: byte
300 |
301 | Msg* = object
302 | hwnd*: pointer
303 | message*: int32
304 | wParam*: int
305 | lParam*: int
306 | time*: int32
307 | pt*: Point
308 |
309 | ActCtx* = object
310 | cbSize*: int32
311 | dwFlags*: int32
312 | lpSource*: cstring
313 | wProcessorArchitecture*: int16
314 | wLangId*: int16
315 | lpAssemblyDirectory*: cstring
316 | lpResourceName*: cstring
317 | lpApplicationName*: cstring
318 | hModule*: pointer
319 |
320 | ScrollInfo* = object
321 | cbSize*: int32
322 | fMask*: int32
323 | nMin*: int32
324 | nMax*: int32
325 | nPage*: int32
326 | nPos*: int32
327 | nTrackPos*: int32
328 |
329 | MonitorInfo * = object
330 | cbSize*: int32
331 | rcMonitor*: Rect
332 | rcWork*: Rect
333 | dwFlags*: int32
334 |
335 | PaintStruct* = array[68, byte]
336 |
337 | KeyState* = array[256, byte]
338 |
339 | GdiplusStartupInput* = object
340 | GdiplusVersion*: int32
341 | DebugEventCallback*: pointer
342 | SuppressBackgroundThread*: bool
343 | SuppressExternalCodecs*: bool
344 |
345 | OpenFileName* = object
346 | lStructSize*: int32
347 | hwndOwner*: pointer
348 | hInstance*: pointer
349 | lpstrFilter*: cstring
350 | lpstrCustomFilter*: cstring
351 | nMaxCustFilter*: int32
352 | nFilterIndex*: int32
353 | lpstrFile*: cstring
354 | nMaxFile*: int32
355 | lpstrFileTitle*: cstring
356 | nMaxFileTitle*: int32
357 | lpstrInitialDir*: cstring
358 | lpstrTitle*: cstring
359 | Flags*: int32
360 | nFileOffset*: int16
361 | nFileExtension*: int16
362 | lpstrDefExt*: cstring
363 | lCustData*: pointer
364 | lpfnHook*: pointer
365 | lpTemplateName*: cstring
366 | # pvReserved: pointer
367 | # dwReserved: int32
368 | # FlagsEx*: int32
369 |
370 | GUID* = object
371 | Data1*: int32
372 | Data2*: int32
373 | Data3*: int32
374 | Data4*: int32
375 |
376 | BitmapData* = object
377 | Width*: int32
378 | Height*: int32
379 | Stride*: int32
380 | PixelFormat*: int32
381 | Scan0*: ptr UncheckedArray[byte]
382 | Reserved: pointer
383 |
384 | BrowseInfo* = object
385 | hwndOwner*: pointer
386 | pidlRoot*: pointer
387 | pszDisplayName*: cstring
388 | lpszTitle*: cstring
389 | ulFlags*: uint
390 | lpfn*: pointer
391 | lParam*: pointer
392 | iImage*: int32
393 |
394 | MinMaxInfo* = object
395 | ptReserved*: Point
396 | ptMaxSize*: Point
397 | ptMaxPosition*: Point
398 | ptMinTrackSize*: Point
399 | ptMaxTrackSize*: Point
400 |
401 | TTrackMouseEvent* {.pure.} = object
402 | cbSize*: int32
403 | dwFlags*: int32
404 | hwndTrack*: pointer
405 | dwHoverTime*: int32
406 |
407 |
408 | # ----------------------------------------------------------------------------------------
409 | # Replacement for Windows Macros
410 | # ----------------------------------------------------------------------------------------
411 |
412 | import math
413 |
414 | proc loWord*(param: pointer): int = cast[int](param) and 0x0000FFFF
415 |
416 | proc hiWord*(param: pointer): int =
417 | result = (cast[int](param) shr 16) and 0xFFFF
418 | if result > 2^15:
419 | result = result - 2^16
420 |
421 |
422 | # ----------------------------------------------------------------------------------------
423 | # Kernel32 Procs
424 | # ----------------------------------------------------------------------------------------
425 |
426 | proc LoadLibraryA*(lpFileName: cstring): pointer {.importc, libKernel32.}
427 | # proc GetModuleHandleA*(lpModuleName: cstring): pointer {.importc, libKernel32.}
428 | proc GetLastError*(): int {.importc, libKernel32.}
429 | # proc CreateActCtxA*(pActCtx: var ActCtx): pointer {.importc, libKernel32.}
430 | # proc GetSystemDirectoryA*(lpBuffer: pointer, uSize: int32): int32 {.importc, libKernel32.}
431 | proc MultiByteToWideChar*(CodePage, dwFlags: int32, lpMultiByteStr: cstring, cbMultiByte: int32, lpWideCharStr: cstring, cchWideChar: int32): int32 {.importc, libKernel32.}
432 | proc WideCharToMultiByte*(CodePage, dwFlags: int32, lpWideCharStr: cstring, cchWideChar: int32, lpMultiByteStr: cstring, cbMultiByte: int32, lpDefaultChar: cstring, lpUsedDefaultChar: pointer): int32 {.importc, libKernel32.}
433 | proc GlobalLock*(hMem: pointer): pointer {.importc, libKernel32.}
434 | proc GlobalUnlock*(hMem: pointer): bool {.importc, libKernel32.}
435 | proc GlobalAlloc*(uFlags, dwBytes: int32): pointer {.importc, libKernel32.}
436 |
437 |
438 | # ----------------------------------------------------------------------------------------
439 | # User32 Procs
440 | # ----------------------------------------------------------------------------------------
441 |
442 | proc MessageBoxW*(hWnd: pointer, lpText, lpCaption: cstring, uType: int) {.importc, libUser32.}
443 | proc RegisterClassExW*(lpwcx: var WndClassEx): int16 {.importc, libUser32.}
444 | proc CreateWindowExW*(dwExStyle: int32, lpClassName, lpWindowName: cstring, dwStyle: int32, x, y, nWidth, nHeight: int, hWndParent, hMenu, hInstance, lpParam: pointer): pointer {.importc, libUser32.}
445 | proc DestroyWindow*(hWnd: pointer): bool {.importc, libUser32.}
446 | proc ShowWindow*(hWnd: pointer, nCmdShow: int32): bool {.importc, libUser32.}
447 | proc EnableWindow*(hWnd: pointer, bEnable: bool): bool {.importc, libUser32.}
448 | proc DefWindowProcW*(hWnd: pointer, uMsg: int, wParam, lParam: pointer): pointer {.importc, libUser32.}
449 | proc GetMessageW*(lpMsg, hWnd: pointer, wMsgFilterMin, wMsgFilterMax: int32): bool {.importc, libUser32.}
450 | proc PeekMessageW*(lpMsg, hWnd: pointer, wMsgFilterMin, wMsgFilterMax, wRemoveMsg: int32): bool {.importc, libUser32.}
451 | proc TranslateMessage*(lpMsg: pointer): bool {.importc, libUser32.}
452 | proc DispatchMessageW*(lpMsg: pointer): pointer {.importc, libUser32.}
453 | proc SetParent*(hWndChild, hWndNewParent: pointer): pointer {.importc, libUser32.}
454 | proc SetWindowLongA*(hWnd: pointer, nIndex, dwNewLong: int32): int32 {.importc, libUser32.}
455 | proc GetWindowLongA*(hWnd: pointer, nIndex: int32): int32 {.importc, libUser32.}
456 | proc SetWindowTextW*(hWnd: pointer, lpString: cstring): bool {.importc, libUser32.}
457 | proc GetWindowTextW*(hWnd: pointer, lpString: cstring, nMaxCount: int32): int32 {.importc, libUser32.}
458 | proc GetWindowTextLengthW*(hWnd: pointer): int32 {.importc, libUser32.}
459 | proc UpdateWindow*(hWnd: pointer): bool {.importc, libUser32.}
460 | proc SetWindowPos*(wnd, hWndInsertAfter: pointer, x, y, cx, cy: int32, uFlags: int): bool {.importc, libUser32.}
461 | proc MoveWindow*(wnd: pointer, x, y, nWidth, nHeight: int32, bRepaint: bool): bool {.importc, libUser32.}
462 | proc SetFocus*(hWnd: pointer): pointer {.importc, libUser32.}
463 | proc GetWindowRect*(wnd: pointer, lpRect: var Rect): bool {.importc, libUser32.}
464 | proc GetClientRect*(wnd: pointer, lpRect: var Rect): bool {.importc, libUser32.}
465 | proc BeginPaint*(hWnd: pointer, lpPaint: var PaintStruct): pointer {.importc, libUser32.}
466 | proc EndPaint*(hWnd: pointer, lpPaint: var PaintStruct): bool {.importc, libUser32.}
467 | proc SendMessageA*(hWnd: pointer, msg: int32, wParam, lParam: pointer): pointer {.importc, libUser32.}
468 | proc SendMessageW*(hWnd: pointer, msg: int32, wParam, lParam: pointer): pointer {.importc, libUser32.}
469 | proc PostMessageA*(hWnd: pointer, msg: int32, wParam, lParam: pointer): bool {.importc, libUser32.}
470 | proc GetSysColor*(nIndex: int32): RGB32 {.importc, libUser32.}
471 | proc InvalidateRect*(hWnd: pointer, lpRect: ref Rect, bErase: bool): bool {.importc, libUser32.}
472 | proc PostQuitMessage*(nExitCode: int32) {.importc, libUser32.}
473 | proc GetDesktopWindow*(): pointer {.importc, libUser32.}
474 | # proc SystemParametersInfoW*(uiAction, uiParam: int32, pvParam: pointer, fWinIni: int32): bool {.importc, libUser32.}
475 | proc ClientToScreen*(hWnd: pointer, lpPoint: var Point): bool {.importc, libUser32.}
476 | proc AdjustWindowRect*(lpRect: var Rect, dwStyle: int32, bMenu: bool): bool {.importc, libUser32.}
477 | proc LoadCursorA*(hInstance: pointer, lpCursorName: cstring): pointer {.importc, libUser32.}
478 | proc SetScrollInfo*(hWnd: pointer, fnBar: int32, lpsi: var ScrollInfo, fRedraw: bool): int32 {.importc, libUser32.}
479 | proc GetMonitorInfoA*(hMonitor: pointer, lpmi: var MonitorInfo): bool {.importc, libUser32.}
480 | proc MonitorFromRect*(lprc: var Rect, dwFlags: int32): pointer {.importc, libUser32.}
481 | proc GetSystemMetrics*(nIndex: int32): int32 {.importc, libUser32.}
482 | proc CallWindowProcW*(lpPrevWndFunc, hWnd: pointer, uMsg: int32, wParam, lParam: pointer): pointer {.importc, libUser32.}
483 | proc IsDialogMessageW*(hDlg, lpMsg: pointer): bool {.importc, libUser32.}
484 | proc GetNextDlgTabItem*(hDlg, hCtl: pointer, bPrevious: bool): pointer {.importc, libUser32.}
485 | proc GetParent*(hWnd: pointer): pointer {.importc, libUser32.}
486 | proc GetDC*(hWnd: pointer): pointer {.importc, libUser32.}
487 | # proc DrawTextW*(hdc: pointer, lpchText: cstring, nCount: int32, lpRect: var Rect, uFormat: int32): int32 {.importc, libUser32.}
488 | proc GetKeyboardState*(lpKeyState: var KeyState): bool {.importc, libUser32.}
489 | proc ToUnicode*(wVirtKey, wScanCode: int32, lpKeyState: var KeyState, pwszBuff: cstring, cchBuff, wFlags: int32): int32 {.importc, libUser32.}
490 | proc ShowScrollBar*(hWnd: pointer, wBar: int32, bShow: bool): bool {.importc, libUser32.}
491 | proc LoadImageW*(hinst: pointer, lpszName: cstring, uType, cxDesired, cyDesired, fuLoad: int32): int32 {.importc, libUser32.}
492 | proc SetTimer*(hWnd, nIDEvent: pointer, uElapse: int32, lpTimerFunc: pointer): pointer {.importc, libUser32.}
493 | proc KillTimer*(hWnd, nIDEvent: pointer): bool {.importc, libUser32.}
494 | proc FillRect*(hDC: pointer, lprc: var Rect, hbr: pointer): int32 {.importc, libUser32.}
495 | proc FrameRect*(hDC: pointer, lprc: var Rect, hbr: pointer): int32 {.importc, libUser32.}
496 | proc GetKeyState*(nVirtKey: int32): int16 {.importc, libUser32.}
497 | proc OpenClipboard*(hWndNewOwner: pointer): bool {.importc, libUser32.}
498 | proc CloseClipboard*(): bool {.importc, libUser32.}
499 | proc GetClipboardData*(uFormat: int32): pointer {.importc, libUser32.}
500 | proc SetClipboardData*(uFormat: int32, hMem: pointer): pointer {.importc, libUser32.}
501 | proc EmptyClipboard*(): bool {.importc, libUser32.}
502 | proc MapVirtualKeyW*(uCode, uMapType: int32): int32 {.importc, libUser32.}
503 | proc GetCursorPos*(lpPoint: var Point): bool {.importc, libUser32.}
504 | proc ScreenToClient*(hWnd: pointer, lpPoint: var Point): bool {.importc, libUser32.}
505 | proc MonitorFromPoint*(pt: Point, dwFlags: int32): pointer {.importc, libUser32.}
506 | proc GetScrollPos*(hWnd: pointer, nBar: int32): int32 {.importc, libUser32.}
507 | proc LockWindowUpdate*(hWndLock: pointer): bool {.importc, libUser32.}
508 | proc TrackMouseEvent*(lpEventTrack: var TTrackMouseEvent): bool {.importc, libUser32.}
509 |
510 | type GetDpiForWindowType* = proc(hWnd: pointer): int32 {.gcsafe, stdcall.} # not available on Windows 7
511 |
512 | when defined(cpu64):
513 | # Only available on 64-bit Windows:
514 | proc GetWindowLongPtrW*(hWnd: pointer, nIndex: int32): pointer {.importc, libUser32.}
515 | proc SetWindowLongPtrW*(hWnd: pointer, nIndex: int32, dwNewLong: pointer): pointer {.importc, libUser32.}
516 | else:
517 | # Should only be used on 32-bit Windows:
518 | proc GetWindowLongW*(hWnd: pointer, nIndex: int32): pointer {.importc, libUser32.}
519 | proc SetWindowLongW*(hWnd: pointer, nIndex: int32, dwNewLong: pointer): pointer {.importc, libUser32.}
520 |
521 |
522 | # ----------------------------------------------------------------------------------------
523 | # GDI Procs
524 | # ----------------------------------------------------------------------------------------
525 |
526 | proc DeleteDC*(hdc: pointer): bool {.importc, libGdi32.}
527 | proc DeleteObject*(hObject: pointer): bool {.importc, libGdi32.}
528 | proc GetCurrentObject*(hdc: pointer, uObjectType: int32): pointer {.importc, libGdi32.}
529 | proc SelectObject*(hdc, hgdiobj: pointer): pointer {.importc, libGdi32.}
530 | proc TextOutW*(hdc: pointer, nXStart, nYStart: int32, lpString: cstring, cchString: int32): bool {.importc, libGdi32.}
531 | proc CreateSolidBrush*(crColor: RGB32): pointer {.importc, libGdi32.}
532 | proc CreatePen*(fnPenStyle, nWidth: int32, crColor: RGB32): pointer {.importc, libGdi32.}
533 | # proc GetStockObject*(fnObject: int32): pointer {.importc, libGdi32.}
534 | proc CreateFontW*(nHeight, nWidth, nEscapement, nOrientation, fnWeight, fdwItalic, fdwUnderline, fdwStrikeOut, fdwCharSet, fdwOutputPrecision, fdwClipPrecision, fdwQuality, fdwPitchAndFamily: int32, lpszFace: cstring): pointer {.importc, libGdi32.}
535 | proc GetTextExtentPoint32W*(hdc: pointer, lpString: cstring, c: int32, lpSize: var Size): bool {.importc, libGdi32.}
536 | proc SetBkMode*(hdc: pointer, iBkMode: int32): int32 {.importc, libGdi32.}
537 | proc SetTextColor*(hdc: pointer, crColor: RGB32): RGB32 {.importc, libGdi32.}
538 | proc SetBkColor*(hdc: pointer, crColor: RGB32): RGB32 {.importc, libGdi32.}
539 | proc MoveToEx*(hdc: pointer, x, y: int32, lpPoint: pointer): bool {.importc, libGdi32.}
540 | proc LineTo*(hdc: pointer, nXEnd, nYEnd: int): bool {.importc, libGdi32.}
541 | proc CreateCompatibleDC*(hdc: pointer): pointer {.importc, libGdi32.}
542 | proc SetPixel*(hdc: pointer, x, y: int32, crColor: RGB32): int32 {.importc, libGdi32.}
543 | # proc BitBlt*(hdcDest: pointer, nXDest, nYDest, nWidth, nHeight: int32, hdcSrc: pointer, nXSrc, nYSrc, dwRop: int32): bool {.importc, libGdi32.}
544 |
545 |
546 | # ----------------------------------------------------------------------------------------
547 | # GDI+ Procs
548 | # ----------------------------------------------------------------------------------------
549 |
550 | proc GdiplusStartup*(token: var pointer, input: var GdiplusStartupInput, output: pointer): int32 {.importc, libGdiplus.}
551 | proc GdipCreateBitmapFromFile*(filename: cstring, bitmap: var pointer): int32 {.importc, libGdiplus.}
552 | # proc GdipLoadImageFromFile*(filename: cstring, image: var pointer): int32 {.importc, libGdiplus.}
553 | proc GdipCreateHICONFromBitmap*(bitmap: pointer, hicon: var pointer): int32 {.importc, libGdiplus.}
554 | proc GdipCreateFromHDC*(hdc: pointer, graphics: var pointer): int32 {.importc, libGdiplus.}
555 | proc GdipDeleteGraphics*(graphics: pointer): int32 {.importc, libGdiplus.}
556 | proc GdipDrawImageRectI*(graphics, image: pointer, x, y, width, height: int32): int32 {.importc, libGdiplus.}
557 | proc GdipGetImageWidth*(image: pointer, width: var int32): int32 {.importc, libGdiplus.}
558 | proc GdipGetImageHeight*(image: pointer, height: var int32): int32 {.importc, libGdiplus.}
559 | # proc GdipGetImageDimension*(image: pointer, width, height: var float): int32 {.importc, libGdiplus.}
560 | proc GdipCreateBitmapFromGraphics*(width, height: int32, target: pointer, bitmap: var pointer): int32 {.importc, libGdiplus.}
561 | proc GdipBitmapSetPixel*(bitmap: pointer, x, y: int32, color: ARGB): int32 {.importc, libGdiplus.}
562 | proc GdipSaveImageToFile*(image: pointer, filename: cstring, clsidEncoder, encoderParams: pointer): int32 {.importc, libGdiplus.}
563 | # proc GdipGetImageEncodersSize*(numEncoders, size: var int32): int32 {.importc, libGdiplus.}
564 | # proc GdipGetImageEncoders*(numEncoders, size: int32, encoders: pointer): int32 {.importc, libGdiplus.}
565 | proc GdipGetImageGraphicsContext*(image: pointer, graphics: var pointer): int32 {.importc, libGdiplus.} # does not exist
566 | proc GdipDisposeImage*(image: pointer): int32 {.importc, libGdiplus.}
567 | proc GdipFillRectangleI*(graphics, brush: pointer, x, y, width, height: int32): int32 {.importc, libGdiplus.}
568 | proc GdipDrawRectangleI*(graphics, pen: pointer, x, y, width, height: int32): int32 {.importc, libGdiplus.}
569 | proc GdipDrawLineI*(graphics, pen: pointer, x1, y1, x2, y2: int32): int32 {.importc, libGdiplus.}
570 | proc GdipDrawArc*(graphics, pen: pointer, x, y, width, height, startAngle, sweepAngle: cfloat): int32 {.importc, libGdiplus.}
571 | proc GdipFillEllipseI*(graphics, brush: pointer, x, y, width, height: int32): int32 {.importc, libGdiplus.}
572 | proc GdipDrawEllipseI*(graphics, pen: pointer, x, y, width, height: int32): int32 {.importc, libGdiplus.}
573 | proc GdipCreateSolidFill*(color: ARGB, brush: var pointer): int32 {.importc, libGdiplus.}
574 | proc GdipDeleteBrush*(brush: pointer): int32 {.importc, libGdiplus.}
575 | proc GdipCreatePen1*(color: ARGB, width: cfloat, unit: int32, pen: var pointer): int32 {.importc, libGdiplus.}
576 | proc GdipDeletePen*(pen: pointer): int32 {.importc, libGdiplus.}
577 | proc GdipDrawString*(graphics: pointer, `string`: cstring, length: int32, font: pointer, layoutRect: var RectF, stringFormat, brush: pointer): int32 {.importc, libGdiplus.}
578 | proc GdipMeasureString*(graphics: pointer, `string`: cstring, length: int32, font: pointer, layoutRect: var RectF, stringFormat: pointer, boundingBox: var RectF, codepointsFitted, linesFilled: pointer): int32 {.importc, libGdiplus.}
579 | proc GdipCreateFont*(fontFamily: pointer, emSize: cfloat, style, unit: int32, font: var pointer): int32 {.importc, libGdiplus.}
580 | proc GdipDeleteFont*(font: pointer): int32 {.importc, libGdiplus.}
581 | proc GdipCreateFontFamilyFromName*(name: cstring, fontCollection: pointer, fontFamily: var pointer): int32 {.importc, libGdiplus.}
582 | proc GdipDeleteFontFamily*(fontFamily: pointer): int32 {.importc, libGdiplus.}
583 | proc GdipBitmapLockBits*(bitmap: pointer, rect: var Rect, flags: int32, format: int32, lockedBitmapData: var BitmapData): int32 {.importc, libGdiplus.}
584 | proc GdipBitmapUnlockBits*(bitmap: pointer, lockedBitmapData: var BitmapData): int32 {.importc, libGdiplus.}
585 | proc GdipSetTextRenderingHint*(graphics: pointer, mode: int32): int32 {.importc, libGdiplus.}
586 | proc GdipSetInterpolationMode*(graphics: pointer, interpolationMode: int32): int32 {.importc, libGdiplus.}
587 |
588 |
589 | # ----------------------------------------------------------------------------------------
590 | # Shell32 Procs
591 | # ----------------------------------------------------------------------------------------
592 |
593 | proc DragAcceptFiles*(hWnd: pointer, fAccept: bool) {.importc, libShell32.}
594 | proc DragQueryFileW*(hDrop: pointer, iFile: uint32, lpszFile: cstring, cch: int32): int32 {.importc, libShell32.}
595 | proc DragFinish*(hDrop: pointer) {.importc, libShell32.}
596 | proc SHBrowseForFolderW*(lpbi: var BrowseInfo): pointer {.importc, libShell32.}
597 | proc SHGetPathFromIDListW*(pidl: pointer, pszPath: cstring) {.importc, libShell32.}
598 |
599 |
600 | # ----------------------------------------------------------------------------------------
601 | # Comdlg32 Procs
602 | # ----------------------------------------------------------------------------------------
603 |
604 | proc CommDlgExtendedError*(): int32 {.importc, libComdlg32.}
605 | proc GetOpenFileNameW*(lpofn: var OpenFileName): bool {.importc, libComdlg32.}
606 | proc GetSaveFileNameW*(lpofn: var OpenFileName): bool {.importc, libComdlg32.}
607 |
608 |
609 | # ----------------------------------------------------------------------------------------
610 | # Shcore Procs
611 | # ----------------------------------------------------------------------------------------
612 |
613 | type SetProcessDpiAwarenessType* = proc(value: int32): int32 {.gcsafe, stdcall.} # not available on Windows 7
614 |
--------------------------------------------------------------------------------