1 /***************************************************************************
2  *   Copyright (c) 2017-2019 by the fifechan team                               *
3  *   https://github.com/fifengine/fifechan                                 *
4  *   This file is part of fifechan.                                        *
5  *                                                                         *
6  *   fifechan is free software; you can redistribute it and/or             *
7  *   modify it under the terms of the GNU Lesser General Public            *
8  *   License as published by the Free Software Foundation; either          *
9  *   version 2.1 of the License, or (at your option) any later version.    *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful,       *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the                 *
18  *   Free Software Foundation, Inc.,                                       *
19  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
20  ***************************************************************************/
21 
22 /*      _______   __   __   __   ______   __   __   _______   __   __
23  *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
24  *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
25  *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
26  *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
27  * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
28  * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
29  *
30  * Copyright (c) 2004 - 2008 Olof Naess�n and Per Larsson
31  *
32  *
33  * Per Larsson a.k.a finalman
34  * Olof Naess�n a.k.a jansem/yakslem
35  *
36  * Visit: http://guichan.sourceforge.net
37  *
38  * License: (BSD)
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in
46  *    the documentation and/or other materials provided with the
47  *    distribution.
48  * 3. Neither the name of Guichan nor the names of its contributors may
49  *    be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
53  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
54  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
55  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
56  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
57  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
58  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
59  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63  */
64 
65 /*
66  * For comments regarding functions please see the header file.
67  */
68 
69 #include <algorithm>
70 
71 #include "fifechan/widgets/window.hpp"
72 
73 #include "fifechan/exception.hpp"
74 #include "fifechan/font.hpp"
75 #include "fifechan/graphics.hpp"
76 #include "fifechan/mouseinput.hpp"
77 
78 namespace fcn
79 {
Window()80     Window::Window()
81             :mMoved(false)
82     {
83         setBorderSize(1);
84         setInnerBorderSize(1);
85         setPadding(2);
86         setTitleBarHeight(16);
87         setAlignment(Graphics::Center);
88         addMouseListener(this);
89         setMovable(true);
90         setOpaque(true);
91     }
92 
Window(const std::string & caption)93     Window::Window(const std::string& caption)
94             :mMoved(false)
95     {
96         setCaption(caption);
97         setBorderSize(1);
98         setInnerBorderSize(1);
99         setPadding(2);
100         setTitleBarHeight(16);
101         setAlignment(Graphics::Center);
102         addMouseListener(this);
103         setMovable(true);
104         setOpaque(true);
105     }
106 
~Window()107     Window::~Window()
108     {
109     }
110 
setTitleBarHeight(unsigned int height)111     void Window::setTitleBarHeight(unsigned int height)
112     {
113         mTitleBarHeight = height;
114     }
115 
getTitleBarHeight() const116     unsigned int Window::getTitleBarHeight() const
117     {
118         return mTitleBarHeight;
119     }
120 
setInnerBorderSize(unsigned int border)121     void Window::setInnerBorderSize(unsigned int border) {
122         mInnerBorder = border;
123     }
124 
getInnerBorderSize() const125     unsigned int Window::getInnerBorderSize() const {
126         return mInnerBorder;
127     }
128 
setCaption(const std::string & caption)129     void Window::setCaption(const std::string& caption)
130     {
131         mCaption = caption;
132     }
133 
getCaption() const134     const std::string& Window::getCaption() const
135     {
136         return mCaption;
137     }
138 
setAlignment(Graphics::Alignment alignment)139     void Window::setAlignment(Graphics::Alignment alignment)
140     {
141         mAlignment = alignment;
142     }
143 
getAlignment() const144     Graphics::Alignment Window::getAlignment() const
145     {
146         return mAlignment;
147     }
148 
drawInnerBorder(Graphics * graphics)149     void Window::drawInnerBorder(Graphics* graphics) {
150         const Color &faceColor = getBaseColor();
151         Color highlightColor, shadowColor;
152         const int alpha = getBaseColor().a;
153         highlightColor = faceColor + 0x303030;
154         highlightColor.a = alpha;
155         shadowColor = faceColor - 0x303030;
156         shadowColor.a = alpha;
157 
158         int x = getBorderSize() + getPaddingLeft();
159         int y = getBorderSize() + getPaddingTop() + getTitleBarHeight();
160         int width = getWidth() - getBorderSize() - getPaddingRight() - 1;
161         int height = getHeight() - getBorderSize() - getPaddingBottom() - 1;
162 
163         unsigned int i;
164         for (i = 0; i < getInnerBorderSize(); ++i) {
165             graphics->setColor(shadowColor);
166             graphics->drawLine(x+i, y+i, width-i, y+i);
167             graphics->drawLine(x+i, y+i+1, x+i, height-i-1);
168             graphics->setColor(highlightColor);
169             graphics->drawLine(width-i, y+i+1, width-i, height-i);
170             graphics->drawLine(x+i, height-i, width-i-1, height-i);
171         }
172     }
173 
draw(Graphics * graphics)174     void Window::draw(Graphics* graphics) {
175         const Color &faceColor = getBaseColor();
176         Color highlightColor, shadowColor;
177         const int alpha = getBaseColor().a;
178         highlightColor = faceColor + 0x303030;
179         highlightColor.a = alpha;
180         shadowColor = faceColor - 0x303030;
181         shadowColor.a = alpha;
182 
183         if (isOpaque()) {
184             // Fill the background around the content
185             graphics->setColor(faceColor);
186             graphics->fillRectangle(getBorderSize(), getBorderSize(),
187                 getWidth() - 2 * getBorderSize(), getHeight() - 2 * getBorderSize());
188         }
189         if (mBackgroundWidget) {
190             Rectangle rec(getBorderSize(), getBorderSize(), getWidth() - 2 * getBorderSize(), getHeight() - 2 * getBorderSize());
191             mBackgroundWidget->setDimension(rec);
192             mBackgroundWidget->_draw(graphics);
193         }
194         if (getBorderSize() > 0) {
195             drawBorder(graphics);
196         }
197 
198         // draw inner/content border
199         if (getInnerBorderSize() > 0) {
200             drawInnerBorder(graphics);
201         }
202 
203         // draw text
204         int textX;
205         int textY = ((int)getTitleBarHeight() - getFont()->getHeight()) / 2;
206 
207         switch (getAlignment())
208         {
209           case Graphics::Left:
210               textX = 0;
211               break;
212           case Graphics::Center:
213               textX = (getWidth() - 2 * getBorderSize() - getPaddingLeft() - getPaddingRight()) / 2;
214               break;
215           case Graphics::Right:
216               textX = getWidth() - getBorderSize() - getPaddingRight();
217               break;
218           default:
219               throw FCN_EXCEPTION("Unknown alignment.");
220         }
221         // text clip area
222         Rectangle rec(getBorderSize() + getPaddingLeft(), getBorderSize() + getPaddingTop(),
223             getWidth() - 2 * getBorderSize() - getPaddingLeft() - getPaddingRight(),
224             getTitleBarHeight() - 1);
225 
226         graphics->setColor(getForegroundColor());
227         graphics->setFont(getFont());
228         graphics->pushClipArea(rec);
229         graphics->drawText(getCaption(), textX, textY, getAlignment());
230         graphics->popClipArea();
231     }
232 
mousePressed(MouseEvent & mouseEvent)233     void Window::mousePressed(MouseEvent& mouseEvent)
234     {
235         if (mouseEvent.getSource() != this)
236         {
237             return;
238         }
239 
240         if (getParent() != NULL)
241         {
242             getParent()->moveToTop(this);
243         }
244 
245         mDragOffsetX = mouseEvent.getX();
246         mDragOffsetY = mouseEvent.getY();
247 
248         int height = getBorderSize() + getPaddingTop() + getTitleBarHeight();
249         mMoved = mouseEvent.getY() <= height;
250     }
251 
mouseReleased(MouseEvent & mouseEvent)252     void Window::mouseReleased(MouseEvent& mouseEvent)
253     {
254         mMoved = false;
255     }
256 
mouseDragged(MouseEvent & mouseEvent)257     void Window::mouseDragged(MouseEvent& mouseEvent)
258     {
259         if (mouseEvent.isConsumed() || mouseEvent.getSource() != this)
260         {
261             return;
262         }
263 
264         if (isMovable() && mMoved)
265         {
266             setPosition(mouseEvent.getX() - mDragOffsetX + getX(),
267                         mouseEvent.getY() - mDragOffsetY + getY());
268         }
269 
270         mouseEvent.consume();
271     }
272 
adjustSize()273     void Window::adjustSize() {
274         resizeToChildren();
275         int w = std::max(getFont()->getWidth(mCaption), getWidth()) + 2 * getBorderSize() + getPaddingLeft() + getPaddingRight() + 2 * getInnerBorderSize();
276         int h = getHeight() + 2 * getBorderSize() + getPaddingTop() + getPaddingBottom() + 2 * getInnerBorderSize() + getTitleBarHeight();
277         setSize(w, h);
278     }
279 
getChildrenArea()280     Rectangle Window::getChildrenArea()
281     {
282         Rectangle rec;
283         rec.x = getBorderSize() + getPaddingLeft() + getInnerBorderSize();
284         rec.y = getBorderSize() + getPaddingTop() + getInnerBorderSize() + getTitleBarHeight();
285         rec.width = getWidth() - 2 * getBorderSize() - getPaddingLeft() - getPaddingRight() - 2*getInnerBorderSize();
286         rec.height = getHeight() - 2 * getBorderSize() - getPaddingTop() - getPaddingBottom() - 2*getInnerBorderSize() - getTitleBarHeight();
287         return rec;
288     }
289 
setMovable(bool movable)290     void Window::setMovable(bool movable)
291     {
292         mMovable = movable;
293     }
294 
isMovable() const295     bool Window::isMovable() const
296     {
297         return mMovable;
298     }
299 
setOpaque(bool opaque)300     void Window::setOpaque(bool opaque)
301     {
302         mOpaque = opaque;
303     }
304 
isOpaque() const305     bool Window::isOpaque() const
306     {
307         return mOpaque;
308     }
309 }
310