1 /* DrawOfPages: Take notes with touchscreen input. 2 * Copyright (C) 2019 Marko Semet 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU Affero General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU Affero General Public License for more details. 13 * 14 * You should have received a copy of the GNU Affero General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 module dop.gtk3.mainwindow; 18 19 private 20 { 21 import cairo.Context; 22 23 import dop.authorship; 24 import dop.processing.mainsystem; 25 import dop.structures.base; 26 27 import gdk.RGBA; 28 29 import gtk.AboutDialog; 30 import gtk.Application; 31 import gtk.ApplicationWindow; 32 import gtk.Builder; 33 import gtk.ColorChooserIF; 34 import gtk.ColorChooserWidget; 35 import gtk.DrawingArea; 36 import gtk.Image; 37 import gtk.Label; 38 import gtk.MenuButton; 39 import gtk.MenuItem; 40 import gtk.Range; 41 import gtk.Scale; 42 import gtk.SpinButton; 43 import gtk.ToggleButton; 44 import gtk.Widget; 45 46 import gobject.ObjectG; 47 import gobject.ParamSpec; 48 49 import std.algorithm : min; 50 import std.format; 51 import std.math : PI; 52 } 53 54 public 55 { 56 /++ 57 + Main window of draw of pages 58 +/ 59 class DOP_MainWindow 60 { 61 private 62 { 63 Builder __builder; 64 shared MainSystem __mainSystem; 65 66 ApplicationWindow __aw; 67 68 Image __colorInfoSizeInfo; 69 DrawingArea __colorInfo; 70 ColorChooserWidget __colorChooser; 71 MenuButton __colorHelper; 72 73 Scale __sizeChooser1; 74 SpinButton __sizeChooser2; 75 Label __sizeInfo; 76 MenuButton __sizeHelper; 77 78 AboutDialog __menuAboutDialog; 79 MenuItem __menuAbout; 80 MenuItem __menuQuit; 81 82 // 83 // Colors 84 // 85 void __updateColor(Context context) 86 { 87 // Center position 88 double posX; 89 double posY; 90 double size; 91 GtkAllocation alloc; 92 { 93 int requestWidth; 94 int requestHeight; 95 int baseLine; 96 97 this.__colorInfo.getSizeRequest(requestWidth, requestHeight); 98 this.__colorInfo.getAllocatedSize(alloc, baseLine); 99 size = min(requestHeight, requestWidth) * 0.4; 100 posX = alloc.width / 2.; 101 posY = alloc.height / 2.; 102 } 103 104 ColorRGBA currentColor; 105 if(this.__mainSystem !is null) 106 { 107 currentColor = this.__mainSystem.currentColor; 108 } 109 { // Draw outer circle 110 context.save(); 111 context.arc(posX, posY, size, 0, 2 * PI); 112 if(currentColor.getLuma() >= 0.5) 113 { 114 context.setSourceRgba(0, 0, 0, 1); 115 } 116 else 117 { 118 context.setSourceRgba(1, 1, 1, 1); 119 } 120 context.fill(); 121 context.restore(); 122 } 123 124 { // Draw inner circle 125 context.save(); 126 context.arc(posX, posY, size * 0.8, 0, 2 * PI); 127 context.setSourceRgba(currentColor.r, currentColor.g, currentColor.b, currentColor.a); 128 context.fill(); 129 context.restore(); 130 } 131 } 132 void __updateColorInfoSize(int width, int height) 133 { 134 int tmpW = 0; 135 int tmpH = 0; 136 this.__colorInfo.getSizeRequest(tmpW, tmpH); 137 if((width != tmpW) || (height != tmpH)) 138 { 139 this.__colorInfo.setSizeRequest(width, height); 140 this.__colorInfo.queueResize(); 141 this.updateColor(); 142 } 143 } 144 } 145 146 public 147 { 148 /++ 149 + Create main window. 150 + Params: 151 + app = The app to use 152 +/ 153 this(Application app) 154 { 155 // Find and show main window 156 this.__builder = new Builder(); 157 this.__builder.setApplication(app); 158 assert(this.__builder.addFromString(import("MainWindow.glade")) != 0); 159 this.__aw = cast(ApplicationWindow) this.__builder.getObject("MainApplication"); 160 assert(this.__aw !is null); 161 this.__aw.setApplication(app); 162 this.__aw.showAll(); 163 164 { // Manage color info 165 this.__colorInfo = cast(DrawingArea) this.__builder.getObject("ColorInfo"); 166 assert(this.__colorInfo !is null); 167 this.__colorInfoSizeInfo = cast(Image) this.__builder.getObject("ColorInfoSizeInfo"); 168 assert(this.__colorInfoSizeInfo !is null); 169 this.__colorChooser = cast(ColorChooserWidget) this.__builder.getObject("ColorChooser"); 170 assert(this.__colorChooser !is null); 171 this.__colorHelper = cast(MenuButton) this.__builder.getObject("ColorHelper"); 172 assert(this.__colorHelper !is null); 173 174 // Color blob size 175 this.__colorInfoSizeInfo.addOnDraw(delegate bool(Context cr, Widget widget) { 176 if(widget == this.__colorInfoSizeInfo) 177 { 178 this.__updateColorInfoSize(this.__colorInfoSizeInfo.getAllocatedWidth(), this.__colorInfoSizeInfo.getAllocatedHeight()); 179 } 180 return false; 181 }); 182 183 // Render color blob 184 this.__colorInfo.addOnDraw(delegate bool(Context cr, Widget widget) { 185 if(widget == this.__colorInfo) 186 { 187 this.__updateColor(cr); 188 } 189 return false; 190 }); 191 192 // Chooser color 193 this.__colorChooser.addOnNotify((delegate void(ParamSpec ps, ObjectG go) { 194 RGBA rgba; 195 this.__colorChooser.getRgba(rgba); 196 ColorRGBA newColor = {rgba.red, rgba.green, rgba.blue, rgba.alpha}; 197 this.__mainSystem.currentColor = newColor; 198 }), "rgba"); 199 this.__colorHelper.addOnToggled(delegate void(ToggleButton button) { 200 if(button == this.__colorHelper && button.getActive()) 201 { 202 this.__colorChooser.setProperty("show-editor", false); 203 RGBA rgba = new RGBA; 204 { 205 ColorRGBA current = this.__mainSystem.currentColor; 206 rgba.red = current.r; 207 rgba.green = current.g; 208 rgba.blue = current.b; 209 rgba.alpha = current.a; 210 } 211 this.__colorChooser.setRgba(rgba); 212 } 213 }); 214 } 215 216 { // Manage point size 217 this.__sizeChooser1 = cast(Scale) this.__builder.getObject("SizeChooser1"); 218 assert(this.__sizeChooser1 !is null); 219 this.__sizeChooser2 = cast(SpinButton) this.__builder.getObject("SizeChooser2"); 220 assert(this.__sizeChooser2 !is null); 221 this.__sizeInfo = cast(Label) this.__builder.getObject("SizeInfo"); 222 assert(this.__sizeInfo !is null); 223 this.__sizeHelper = cast(MenuButton) this.__builder.getObject("SizeHelper"); 224 assert(this.__sizeHelper !is null); 225 226 // Value change callback 227 this.__sizeChooser1.addOnValueChanged(delegate void(Range range) { 228 if(this.__sizeChooser1 == range) 229 { 230 this.__mainSystem.currentSize = this.__sizeChooser1.getValue(); 231 } 232 }); 233 234 // When open reset value 235 this.__sizeHelper.addOnToggled(delegate void(ToggleButton button) { 236 if(button == this.__sizeHelper && button.getActive()) 237 { 238 this.__sizeChooser1.setValue(this.__mainSystem.currentSize); 239 this.__sizeChooser2.setValue(this.__mainSystem.currentSize); 240 } 241 }); 242 } 243 244 { // Manage menu entries 245 this.__menuAboutDialog = cast(AboutDialog) this.__builder.getObject("AboutDialog"); 246 assert(this.__menuAboutDialog !is null); 247 this.__menuAbout = cast(MenuItem) this.__builder.getObject("MenuInfo"); 248 assert(this.__menuAbout !is null); 249 this.__menuQuit = cast(MenuItem) this.__builder.getObject("MenuQuit"); 250 assert(this.__menuQuit !is null); 251 252 // Set infos 253 this.__menuAboutDialog.setArtists(authorship_artists); 254 this.__menuAboutDialog.setAuthors(authorship_authors); 255 this.__menuAboutDialog.setDocumenters(authorship_documenters); 256 { 257 string trans = format!("%(%s\n%)")(authorship_translators); 258 if(trans.length == 0) 259 { 260 this.__menuAboutDialog.setTranslatorCredits(null); 261 } 262 else 263 { 264 this.__menuAboutDialog.setTranslatorCredits(trans[0..$-1]); 265 } 266 } 267 { 268 string copyright = authorship_copyright_infos; 269 if(copyright.length > 0) 270 { 271 copyright ~= "\n\n"; 272 } 273 copyright ~= "Used library: GtkD(http://gtkd.org)"; 274 this.__menuAboutDialog.setCopyright(copyright); 275 } 276 this.__menuAboutDialog.setVersion(programm_version); 277 278 // About 279 this.__menuAbout.addOnActivate(delegate void(MenuItem mi) { 280 if(mi == this.__menuAbout) 281 { 282 this.__menuAboutDialog.showAll(); 283 } 284 }); 285 286 // Quit 287 this.__menuQuit.addOnActivate(delegate void(MenuItem mi) { 288 if(mi == this.__menuQuit) 289 { 290 this.__mainSystem.quit(); 291 } 292 }); 293 } 294 } 295 296 /++ 297 + Returns the main System 298 + Returns: The main system. 299 +/ 300 @property 301 shared(MainSystem) mainSystem() 302 { 303 return this.__mainSystem; 304 } 305 /++ 306 + Sets the main system. It have to be not null 307 + Params: 308 + ms = The main system to set 309 + Returns: The main system 310 +/ 311 @property 312 shared(MainSystem) mainSystem(shared MainSystem ms) 313 in 314 { 315 assert(ms !is null); 316 } 317 do 318 { 319 this.__mainSystem = ms; 320 return ms; 321 } 322 323 /++ 324 + Request to update the color 325 +/ 326 void updateColor() 327 { 328 this.__colorInfo.queueDraw(); 329 } 330 331 /++ 332 + Update size output 333 +/ 334 void updateSize(float size) 335 { 336 this.__sizeInfo.setLabel(format!("%1.2f")(size)); 337 } 338 } 339 } 340 }