Passed
Pull Request — master (#291)
by Marek
02:40
created

Application._processMouseButtonDown()   F

Complexity

Conditions 15

Size

Total Lines 38
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 29
nop 2
dl 0
loc 38
rs 2.9998
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like pygameui.Application.Application._processMouseButtonDown() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#
2
#  Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]
3
#
4
#  This file is part of Pygame.UI.
5
#
6
#  Pygame.UI is free software; you can redistribute it and/or modify
7
#  it under the terms of the Lesser GNU General Public License as published by
8
#  the Free Software Foundation; either version 2.1 of the License, or
9
#  (at your option) any later version.
10
#
11
#  Pygame.UI 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
14
#  Lesser GNU General Public License for more details.
15
#
16
#  You should have received a copy of the Lesser GNU General Public License
17
#  along with Pygame.UI; if not, write to the Free Software
18
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
#
20
21
import pygame
22
import pygame, pygame.time, pygame.mouse
23
24
import SkinableTheme
25
from Tooltip import Tooltip
26
import Const
27
28
# enable only if you want OpenGL support
29
#try:
30
#    from OpenGL.GL import *
31
#    from OpenGL.GLU import *
32
#except ImportError:
33
#    pass
34
35
def noop():
36
    pass
37
38
class Application:
39
40
    def __init__(self, update = noop, theme = SkinableTheme):
41
        self.theme = theme
42
        self.theme.init()
43
        self.updateFunc = update
44
        self.showBackground = True
45
        self.background = None
46
        self.redrawWidgets = {}
47
        self.cursorPos = (0, 0)
48
        self.windowSurfaceFlags = 0
49
        # status bar widget
50
        self.statusBar = None
51
        self.statusBarText = None
52
        # internal properties
53
        self.locale = 'en'
54
        self.windows = []
55
        self.focusedWindow = None
56
        self.activeWidget = None
57
        self.mouseOverWidget = None
58
        self.mouseOverCount = 0
59
        self.mouseOverThreshold = 3
60
        self.mouseLMBDouble = 0
61
        self.mouseRMBDouble = 0
62
        self.keyEvt = None
63
        self.keyCount = 0
64
        self.tooltip = Tooltip(self)
65
        self.focusedWidget = None
66
        self.cursorOn = 0
67
        self.cursorCount = 0
68
        self._fullUpdate = False
69
        # setup timer
70
        try:
71
             pygame.time.set_timer(Const.TIMEREVENT, 80)
72
        except pygame.error:
73
            pass
74
75
    def getApp(self):
76
        return self
77
78
    def _processTimerEvent(self, evt):
79
        # tooltips
80
        self.mouseOverCount += 1
81
        if self.mouseOverCount == self.mouseOverThreshold:
82
            # show tooltip
83
            if self.mouseOverWidget:
84
                self.tooltip.title = self.mouseOverWidget.tooltipTitle
85
                self.tooltip.text = self.mouseOverWidget.tooltip
86
                self.tooltip.rect = pygame.Rect(pygame.mouse.get_pos(), (100, 100))
87
        # cursor
88
        self.cursorCount += 1
89
        if self.cursorCount == 5:
90
            self.cursorOn = not self.cursorOn
91
            self.cursorCount = 0
92
            if self.focusedWidget:
93
                self.focusedWidget.onCursorChanged()
94
        # keyboard repeat
95
        if self.keyEvt:
96
            self.keyCount += 1
97
            if self.keyCount == 6:
98
                self.processEvent(self.keyEvt)
99
                self.keyCount = 4
100
        return Const.NoEvent
101
102
    def _processMouseButtonDown(self, evt):
103
        # mouse wheel
104
        if evt.button == 4 or evt.button == 5:
105
            # TODO find window to deliver mouse wheel events to
106
            if self.focusedWindow:
107
                if self.focusedWindow.rect.collidepoint(evt.pos):
108
                    if evt.button == 4:
109
                        return self.focusedWindow.processMWUp(evt)
110
                    elif evt.button == 5:
111
                        return self.focusedWindow.processMWDown(evt)
112
            else:
113
                return evt
114
            return Const.NoEvent
115
        # TODO double click
116
        # check if focused window is top level one
117
        if self.focusedWindow != self.windows[-1]:
118
            window = self.focusedWindow
119
            self.focusWindowAt(evt)
120
            # consume event, when focus has been changed
121
            if self.focusedWindow != window:
122
                return Const.NoEvent
123
        # left and right mouse button
124
        if self.focusedWindow:
125
            if self.focusedWindow.rect.collidepoint(evt.pos):
126
                if evt.button == 1:
127
                    return self.focusedWindow.processMB1Down(evt)
128
                elif evt.button == 3:
129
                    return self.focusedWindow.processMB3Down(evt)
130
            elif self.focusedWindow.modal:
131
                return Const.NoEvent
132
            elif self.focusedWindow.looseFocusClose:
133
                self.focusedWindow.hide()
134
                return self.focusWindowAt(evt)
135
            else:
136
                return self.focusWindowAt(evt)
137
        else:
138
            return self.focusWindowAt(evt)
139
        return evt
140
141
    def _processMouseButtonUp(self, evt):
142
        # left and right mouse button
143
        if self.focusedWindow and self.focusedWindow.rect.collidepoint(evt.pos):
144
            if evt.button == 1:
145
                return self.focusedWindow.processMB1Up(evt)
146
            elif evt.button == 3:
147
                return self.focusedWindow.processMB3Up(evt)
148
        return evt
149
150
    def _processMouseMotion(self, evt):
151
        if self.mouseOverCount < self.mouseOverThreshold:
152
            # just moving across widget does not trigger tooltip
153
            self.mouseOverCount = 0
154
        self.cursorPos = evt.pos
155
        if self.focusedWindow:
156
            return self.focusedWindow.processMMotion(evt)
157
        return evt
158
159
    def _processKeyDown(self, evt):
160
        self.keyEvt = evt
161
        self.keyCount = 0
162
        if self.focusedWidget:
163
            evt = self.focusedWidget.processKeyDown(evt)
164
        if evt != Const.NoEvent and self.focusedWindow:
165
            evt = self.focusedWindow.processKeyDown(evt)
166
        return evt
167
168
    def _processKeyUp(self, evt):
169
        self.keyEvt = None
170
        if self.focusedWidget:
171
            evt = self.focusedWidget.processKeyUp(evt)
172
        if evt != Const.NoEvent and self.focusedWindow:
173
            evt = self.focusedWindow.processKeyUp(evt)
174
        return evt
175
176
    def processEvent(self, evt):
177
        if evt.type == pygame.VIDEOEXPOSE:
178
            self.performFullUpdate()
179
        if not pygame.key.get_focused():
180
            return Const.NoEvent
181
        elif evt.type == Const.TIMEREVENT:
182
            self._processTimerEvent(evt)
183
        elif evt.type == pygame.MOUSEBUTTONDOWN:
184
            self._processMouseButtonDown(evt)
185
        elif evt.type == pygame.MOUSEBUTTONUP:
186
            self._processMouseButtonUp(evt)
187
        elif evt.type == pygame.MOUSEMOTION:
188
            self._processMouseMotion(evt)
189
        elif evt.type == pygame.KEYDOWN:
190
            self._processKeyDown(evt)
191
        elif evt.type == pygame.KEYUP:
192
            self._processKeyUp(evt)
193
        else:
194
            return evt
195
196
    def registerWindow(self, window):
197
        self.windows.append(window)
198
        self._fullUpdate = True
199
200
    def unregisterWindow(self, window):
201
        if window == self.focusedWindow:
202
            self.focusedWindow.hide()
203
        self.windows.remove(window)
204
        self._fullUpdate = True
205
206
    def moveWindowToFront(self, window):
207
        self.focusWindow(window)
208
        if window in self.windows:
209
            self.windows.remove(window)
210
        self.windows.append(window)
211
        self.performFullUpdate()
212
213
    def focusWindow(self, window):
214
        if self.focusedWindow:
215
            self.focusedWindow.focused = 0
216
        self.focusedWindow = window
217
        self.setFocus(None)
218
        if self.focusedWindow:
219
            self.focusedWindow.focused = 1
220
221
    def hideWindow(self, window):
222
        window.visible = 0
223
        self.performFullUpdate()
224
        self._fullUpdate = True
225
        if self.focusedWindow == window:
226
            self.focusedWindow.focused = 0
227
            self.focusedWindow = None
228
            # also unfocus widget
229
            self.setFocus(None)
230
            # find new window to focus
231
            index = len(self.windows) - 1
232
            while index >= 0:
233
                window = self.windows[index]
234
                if window.visible:
235
                    window.toFront()
236
                    return
237
                index -= 1
238
239
    def focusWindowAt(self, evt):
240
        # find window which has been clicked in
241
        index = len(self.windows) - 1
242
        while index >= 0:
243
            window = self.windows[index]
244
            if window.visible and window.rect.collidepoint(evt.pos):
245
                window.toFront()
246
                return Const.NoEvent
247
            index -= 1
248
        return evt
249
250
    def setFocus(self, widget):
251
        if self.focusedWidget != widget:
252
            if self.focusedWidget:
253
                self.focusedWidget.onFocusLost()
254
            self.focusedWidget = widget
255
            if widget:
256
                widget.onFocusGained()
257
258
    def setMouseOver(self, widget):
259
        if self.mouseOverWidget != widget:
260
            if self.mouseOverWidget:
261
                self.mouseOverWidget.onMouseOut()
262
                self.tooltip.text = None
263
                self.tooltip.title = None
264
                self.performFullUpdate()
265
            self.mouseOverWidget = widget
266
            self.mouseOverCount = 0
267
            if widget:
268
                widget.onMouseOver()
269
                self.tooltip.text = None
270
                self.tooltip.title = None
271
                self.performFullUpdate()
272
                widget.parent.setTempStatus(widget.statustip)
273
                return
274
            self.setTempStatus(None)
275
276
    def setStatus(self, text):
277
        self.statusBarText = text
278
        if self.statusBar and self.statusBar.text != text:
279
            self.statusBar.text = text
280
            self.redraw(self.statusBar)
281
282
    def setTempStatus(self, text):
283
        if self.statusBar:
284
            if text:
285
                self.statusBar.text = text
286
            else:
287
                self.statusBar.text = self.statusBarText
288
289
    def draw(self, surface):
290
        """Draw all windows onto supplied surface."""
291
        if self.showBackground:
292
            surface.blit(self.background, (0, 0))
293
        changed = []
294
        #@print "App Draw"
295
        for window in self.windows:
296
            if window.visible:
297
                if self.showBackground:
298
                    window._fullUpdate = True
299
                rect = window.draw(surface)
300
                #@print " ", window, rect
301
                if rect: changed.append(rect)
302
                window.__dict__['_changeReported'] = 0
303
            else:
304
                #@print " ", window, "invisible"
305
                pass
306
        if self.tooltip.title or self.tooltip.text:
307
            title, body = self.theme.drawTooltip(surface, self.tooltip)
308
            changed.append(title)
309
            changed.append(body)
310
        self.tooltip.__dict__['_changeReported'] = 0
311
        self.redrawWidgets = {}
312
        #@print "CHANGED", changed
313
        if self._fullUpdate or self.showBackground:
314
            #@print "FULL UPDATE"
315
            self._fullUpdate = False
316
            return [pygame.display.get_surface().get_rect()]
317
        return changed
318
319
    def drawOpenGL(self):
320
        for window in self.windows:
321
            if window.visible:
322
                window.draw(None)
323
                bitmap = pygame.image.tostring(window.surface, "RGBA", 1)
324
                width, height = window.surface.get_size()
325
                scrW, scrH = pygame.display.get_surface().get_size()
326
                x, y = window.rect.bottomleft
327
                glRasterPos2i(x, scrH - y)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable glRasterPos2i does not seem to be defined.
Loading history...
328
                glDrawPixels(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable glDrawPixels does not seem to be defined.
Loading history...
329
                    width, height,
330
                    GL_RGBA,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable GL_RGBA does not seem to be defined.
Loading history...
331
                    GL_UNSIGNED_BYTE,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable GL_UNSIGNED_BYTE does not seem to be defined.
Loading history...
332
                    bitmap,
333
                )
334
        glFlush()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable glFlush does not seem to be defined.
Loading history...
335
336
    def performFullUpdate(self):
337
        for window in self.windows:
338
            window._fullUpdate = True
339
340
    def drawCursor(self, surface):
341
        self.theme.drawCursor(surface, self.cursorPos)
342
343
    def update(self):
344
        if self.updateFunc:
345
            self.updateFunc()
346
347
    def redraw(self, widget, redrawParent = 0):
348
        self.redrawWidgets[widget] = None
349
350
    def needsUpdate(self):
351
        return len(self.redrawWidgets) > 0
352
353
    def exitLocal(self):
354
        evt = pygame.event.Event(Const.USEREVENT)
355
        evt.action = "localExit"
356
        pygame.event.post(evt)
357
358
    def exit(self):
359
        pygame.event.post(pygame.event.Event(pygame.QUIT))
360
361
    def processAction(self, actionName, data = None, widget = None):
362
        """ There are no application wide actions supported yet."""
363
        return
364