1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Quick Dialogs module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40import QtQuick 2.2
41import QtQuick.Controls 1.2
42import QtQuick.Controls.Private 1.0
43import QtQuick.Controls.Styles 1.0
44import QtQuick.Dialogs 1.1
45import QtQuick.Dialogs.Private 1.1
46import QtQuick.Layouts 1.1
47import QtQuick.Window 2.1
48import "qml"
49
50AbstractFontDialog {
51    id: root
52
53    property alias font: content.externalFont
54    property alias currentFont: content.font
55
56    Rectangle {
57        id: content
58        SystemPalette { id: palette }
59
60        implicitWidth: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 100, mainLayout.implicitWidth + outerSpacing * 2))
61        implicitHeight: Math.min(root.__maximumDimension, Math.max(Screen.pixelDensity * 60, mainLayout.implicitHeight + outerSpacing * 2))
62        property real spacing: 6
63        property real outerSpacing: 12
64        color: palette.window
65
66        property font font: Qt.font({ family: "Helvetica", pointSize: 24, weight: Font.Normal })
67        property font externalFont
68        property string writingSystem
69        property string writingSystemSample
70        property var pointSizes
71
72        onExternalFontChanged: {
73            if (Component.status != Component.Ready)
74                return
75
76            if (content.font != content.externalFont) {
77                font = externalFont
78                wsComboBox.reset()
79                fontListView.reset()
80                weightListView.reset()
81            }
82        }
83
84        Component.onCompleted: externalFontChanged()
85
86        onWritingSystemSampleChanged: { sample.text = writingSystemSample; }
87
88        Keys.onPressed: {
89            event.accepted = true
90            switch (event.key) {
91            case Qt.Key_Return:
92            case Qt.Key_Select:
93                updateUponAccepted()
94                break
95            case Qt.Key_Escape:
96            case Qt.Key_Back:
97                reject()
98                break
99            default:
100                // do nothing
101                event.accepted = false
102                break
103            }
104        }
105
106        function updateUponAccepted() {
107            root.font = content.font
108            root.accept()
109        }
110
111        ColumnLayout {
112            id: mainLayout
113            anchors { fill: parent; margins: content.outerSpacing }
114            spacing: content.spacing
115
116            GridLayout {
117                columnSpacing: content.spacing; rowSpacing: content.spacing
118                columns: 3
119
120                Label { id: fontNameLabel; Layout.fillWidth: true; text: qsTr("Font"); font.bold: true }
121                Label { id: weightLabel; text: qsTr("Weight"); font.bold: true }
122                Label { id: sizeLabel; text: qsTr("Size"); font.bold: true }
123                TableView {
124                    id: fontListView
125                    focus: true
126                    Layout.fillWidth: true
127                    Layout.fillHeight: true
128                    Layout.preferredWidth: fontColumn.width
129                    headerVisible: false
130                    function reset() {
131                        fontModel.findIndex()
132                        content.pointSizes = fontModel.pointSizes()
133                        fontModel.findPointSizesIndex()
134                    }
135                    TableViewColumn{ id: fontColumn; role: "family"; title: qsTr("Font Family") }
136                    model: FontListModel {
137                        id: fontModel
138                        scalableFonts: root.scalableFonts
139                        nonScalableFonts: root.nonScalableFonts
140                        monospacedFonts: root.monospacedFonts
141                        proportionalFonts: root.proportionalFonts
142                        Component.onCompleted: fontListView.reset()
143                        onModelReset: { findIndex(); }
144                        function findIndex() {
145                            fontListView.selection.clear()
146                            if (fontModel.count <= 0 || fontListView.rowCount <= 0)
147                                return
148
149                            var currentRow = 0
150                            if (content.font.family != "") {
151                                for (var i = 0; i < fontModel.count; ++i) {
152                                    if (content.font.family == fontModel.get(i).family) {
153                                        currentRow = i
154                                        break
155                                    }
156                                }
157                            }
158                            content.font.family = fontModel.get(currentRow).family
159                            fontListView.selection.select(currentRow)
160                            fontListView.positionViewAtRow(currentRow, ListView.Contain)
161                            fontListView.clicked(currentRow)
162                        }
163                        function findPointSizesIndex() {
164                            pointSizesListView.selection.clear()
165                            if (content.pointSizes.length <= 0 || pointSizesListView.rowCount <= 0)
166                                return
167
168                            var currentRow = -1
169                            for (var i = 0; i < content.pointSizes.length; ++i) {
170                                if (content.font.pointSize == content.pointSizes[i]) {
171                                    currentRow = i
172                                    break
173                                }
174                            }
175                            if (currentRow != -1) {
176                                content.font.pointSize = content.pointSizes[currentRow]
177                                pointSizesListView.selection.select(currentRow)
178                                pointSizesListView.positionViewAtRow(currentRow, ListView.Contain)
179                                pointSizesListView.clicked(currentRow)
180                            }
181                        }
182                    }
183                    function select(row) {
184                        if (row == -1)
185                            return
186                        currentRow = row
187                        content.font.family = fontModel.get(row).family
188                        positionViewAtRow(row, ListView.Contain)
189                    }
190                    onClicked: select(row)
191                    onCurrentRowChanged: select(currentRow)
192                }
193                TableView {
194                    id: weightListView
195                    implicitWidth: (Component.status == Component.Ready) ? (weightColumn.width + content.outerSpacing) : (100)
196                    Layout.fillHeight: true
197                    Component.onCompleted: resizeColumnsToContents();
198                    headerVisible: false
199                    function reset() {
200                        weightModel.findIndex()
201                    }
202                    TableViewColumn { id: weightColumn; role: "name"; title: qsTr("Weight") }
203                    model: ListModel {
204                        id: weightModel
205                        ListElement { name: qsTr("Thin"); weight: Font.Thin }
206                        ListElement { name: qsTr("ExtraLight"); weight: Font.ExtraLight }
207                        ListElement { name: qsTr("Light"); weight: Font.Light }
208                        ListElement { name: qsTr("Normal"); weight: Font.Normal }
209                        ListElement { name: qsTr("Medium"); weight: Font.Medium }
210                        ListElement { name: qsTr("DemiBold"); weight: Font.DemiBold }
211                        ListElement { name: qsTr("Bold"); weight: Font.Bold }
212                        ListElement { name: qsTr("ExtraBold"); weight: Font.ExtraBold }
213                        ListElement { name: qsTr("Black"); weight: Font.Black }
214                        Component.onCompleted: weightListView.reset()
215                        function findIndex() {
216                            var currentRow = 1
217                            for (var i = 0; i < weightModel.count; ++i) {
218                                if (content.font.weight == weightModel.get(i).weight) {
219                                    currentRow = i
220                                    break
221                                }
222                            }
223                            content.font.weight = weightModel.get(currentRow).family
224                            weightListView.selection.select(currentRow)
225                            weightListView.positionViewAtRow(currentRow, ListView.Contain)
226                            weightListView.clicked(currentRow)
227                        }
228                    }
229                    function select(row) {
230                        if (row == -1)
231                            return
232                        currentRow = row
233                        content.font.weight = weightModel.get(row).weight
234                        positionViewAtRow(row, ListView.Contain)
235                    }
236                    onClicked: select(row)
237                    onCurrentRowChanged: select(currentRow)
238                }
239                ColumnLayout {
240                    SpinBox {
241                        id: pointSizeSpinBox;
242                        implicitWidth: (Component.status == Component.Ready) ? (psColumn.width + content.outerSpacing) : (80)
243                        value: content.font.pointSize
244                        decimals: 0
245                        minimumValue: 1
246                        maximumValue: 512
247                        onValueChanged: {
248                            content.font.pointSize = Number(value);
249                            updatePointSizesIndex();
250                        }
251                        function updatePointSizesIndex() {
252                            pointSizesListView.selection.clear()
253                            if (content.pointSizes.length <= 0 || pointSizesListView.rowCount <= 0)
254                                return
255                            var currentRow = -1
256                            for (var i = 0; i < content.pointSizes.length; ++i) {
257                                if (content.font.pointSize == content.pointSizes[i]) {
258                                    currentRow = i
259                                    break
260                                }
261                            }
262                            if (currentRow < 0)
263                                return
264                            content.font.pointSize = content.pointSizes[currentRow]
265                            pointSizesListView.selection.select(currentRow)
266                            pointSizesListView.positionViewAtRow(currentRow, ListView.Contain)
267                            pointSizesListView.clicked(currentRow)
268                        }
269                    }
270                    TableView {
271                        id: pointSizesListView
272                        Layout.fillHeight: true
273                        headerVisible: false
274                        implicitWidth: (Component.status == Component.Ready) ? (psColumn.width + content.outerSpacing) : (80)
275                        Component.onCompleted: resizeColumnsToContents();
276                        TableViewColumn{ id: psColumn; role: ""; title: qsTr("Size") }
277                        model: content.pointSizes
278                        property bool guard: false
279                        function select(row) {
280                            if (row == -1 || !guard)
281                                return
282                            currentRow = row
283                            content.font.pointSize = content.pointSizes[row]
284                            pointSizeSpinBox.value = content.pointSizes[row]
285                            positionViewAtRow(row, ListView.Contain)
286                        }
287                        onClicked: select(row)
288                        onCurrentRowChanged: {
289                            select(currentRow)
290                            if (!guard)
291                                guard = true
292                        }
293                    }
294                }
295            }
296
297            RowLayout {
298                spacing: content.spacing
299                Layout.fillHeight: false
300                ColumnLayout {
301                    spacing: content.spacing
302                    Layout.rowSpan: 3
303                    Label { id: optionsLabel; text: qsTr("Style"); font.bold: true }
304                    CheckBox {
305                        id: italicCheckBox
306                        text: qsTr("Italic")
307                        checked: content.font.italic
308                        onClicked: { content.font.italic = italicCheckBox.checked }
309                    }
310                    CheckBox {
311                        id: underlineCheckBox
312                        text: qsTr("Underline")
313                        checked: content.font.underline
314                        onClicked: { content.font.underline = underlineCheckBox.checked }
315                    }
316                    CheckBox {
317                        id: overlineCheckBox
318                        text: qsTr("Overline")
319                        checked: content.font.overline
320                        onClicked: { content.font.overline = overlineCheckBox.checked }
321                    }
322                    CheckBox {
323                        id: strikeoutCheckBox
324                        text: qsTr("Strikeout")
325                        checked: content.font.strikeout
326                        onClicked: { content.font.strikeout = strikeoutCheckBox.checked }
327                    }
328                    Item { Layout.fillHeight: true; } //spacer
329                    Label { id: writingSystemLabel; text: qsTr("Writing System"); font.bold: true }
330                }
331
332                ColumnLayout {
333                    Layout.rowSpan: 3
334                    spacing: content.spacing
335                    Layout.columnSpan: 2
336                    Layout.fillWidth: true
337                    Layout.fillHeight: true
338                    Label { id: sampleLabel; text: qsTr("Sample"); font.bold: true }
339
340                    Rectangle {
341                        clip: true
342                        Layout.fillWidth: true
343                        Layout.fillHeight: true
344                        implicitWidth: Math.min(360, sample.implicitWidth + parent.spacing)
345                        implicitHeight: Math.min(240, sample.implicitHeight + parent.spacing)
346                        color: "white"
347                        border.color: "#999"
348                        TextInput {
349                            id: sample
350                            activeFocusOnTab: true
351                            Accessible.name: text
352                            Accessible.role: Accessible.EditableText
353                            anchors.centerIn: parent
354                            font: content.font
355                            onFocusChanged: if (!focus && sample.text == "") sample.text = content.writingSystemSample
356                            renderType: Settings.isMobile ? Text.QtRendering : Text.NativeRendering
357                        }
358                    }
359                }
360            }
361
362            RowLayout {
363                id: buttonRow
364                Layout.columnSpan: 3
365                spacing: content.spacing
366                ComboBox {
367                    id: wsComboBox
368                    function reset() {
369                        if (wsModel.count > 0) {
370                            currentIndex = 0
371                        }
372                    }
373                    textRole: "name"
374                    model: WritingSystemListModel {
375                        id: wsModel
376                        Component.onCompleted: wsComboBox.reset()
377                    }
378                    onCurrentIndexChanged: {
379                        if (currentIndex == -1)
380                            return
381
382                        content.writingSystem = wsModel.get(currentIndex).name
383                        fontModel.writingSystem = content.writingSystem
384                        content.writingSystemSample = wsModel.get(currentIndex).sample
385                        fontListView.reset()
386                    }
387                }
388                Item { Layout.fillWidth: true; } //spacer
389                Button {
390                    text: qsTr("Cancel")
391                    onClicked: root.reject()
392                }
393                Button {
394                    text: qsTr("OK")
395                    onClicked: {
396                        content.updateUponAccepted()
397                    }
398                }
399            }
400        }
401    }
402}
403
404