DefaultFontDialog.qml 19 KB

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