2626_logger = logging .getLogger ('Calculate' )
2727
2828import gi
29- gi .require_version ('Gtk' , '3 .0' )
29+ gi .require_version ('Gtk' , '4 .0' )
3030from gi .repository import Gtk
3131from gi .repository import Gdk
3232import base64
3333
34- import sugar3 .profile
35- from sugar3 .graphics .xocolor import XoColor
34+ import sugar4 .profile
35+ from sugar4 .graphics .xocolor import XoColor
36+ from sugar4 .graphics import style as sugar_style
3637
3738from shareable_activity import ShareableActivity
3839from layout import CalcLayout
@@ -64,8 +65,7 @@ def findchar(text, chars, ofs=0):
6465
6566def _textview_realize_cb (widget ):
6667 '''Change textview properties once window is created.'''
67- win = widget .get_window (Gtk .TextWindowType .TEXT )
68- win .set_cursor (Gdk .Cursor .new (Gdk .CursorType .HAND1 ))
68+ widget .set_cursor_from_name ("pointer" )
6969 return False
7070
7171
@@ -194,7 +194,7 @@ def create_lasteq_textbuf(self):
194194 tagsmallnarrow = buf .create_tag (font = CalcLayout .FONT_SMALL_NARROW )
195195 tagbignarrow = buf .create_tag (font = CalcLayout .FONT_BIG_NARROW )
196196 tagbigger = buf .create_tag (font = CalcLayout .FONT_BIGGER )
197- tagjustright = buf .create_tag (justification = Gtk .Justification .RIGHT )
197+ tagjustright = buf .create_tag (justification = Gtk .Justification .LEFT )
198198 tagred = buf .create_tag (foreground = '#FF0000' )
199199
200200 # Add label and equation
@@ -244,32 +244,50 @@ def create_history_object(self):
244244 return self .result .get_image ()
245245
246246 w = Gtk .TextView ()
247- w .modify_base (
248- Gtk .StateType .NORMAL , Gdk .color_parse (self .color .get_fill_color ()))
249- w .modify_bg (
250- Gtk .StateType .NORMAL ,
251- Gdk .color_parse (self .color .get_stroke_color ()))
247+ w .set_editable (False )
248+ w .set_cursor_visible (False )
249+
250+ fill_color = self .color .get_fill_color ()
251+ stroke_color = self .color .get_stroke_color ()
252+
253+ css_str = """
254+ textview {
255+ background-color: %s;
256+ color: %s;
257+ }
258+ textview text {
259+ background-color: %s;
260+ color: %s;
261+ min-height: 20px;
262+ }
263+ """ % (stroke_color , fill_color , stroke_color , fill_color )
264+
265+ sugar_style .apply_css_to_widget (w , css_str )
266+
252267 w .set_wrap_mode (Gtk .WrapMode .WORD_CHAR )
253- w .set_border_window_size ( Gtk . TextWindowType . LEFT , 4 )
254- w .set_border_window_size ( Gtk . TextWindowType . RIGHT , 4 )
255- w .set_border_window_size ( Gtk . TextWindowType . TOP , 4 )
256- w .set_border_window_size ( Gtk . TextWindowType . BOTTOM , 4 )
268+ w .set_left_margin ( 4 )
269+ w .set_right_margin ( 4 )
270+ w .set_top_margin ( 4 )
271+ w .set_bottom_margin ( 4 )
257272 w .connect ('realize' , _textview_realize_cb )
258273 buf = w .get_buffer ()
259274
260275 tagsmall = buf .create_tag (font = CalcLayout .FONT_SMALL )
261276 tagsmallnarrow = buf .create_tag (font = CalcLayout .FONT_SMALL_NARROW )
262277 tagbig = buf .create_tag (font = CalcLayout .FONT_BIG ,
263- justification = Gtk .Justification .RIGHT )
264- # TODO Fix for old Sugar 0.82 builds, red_float not available
265- bright = (
266- Gdk .color_parse (self .color .get_fill_color ()).red_float +
267- Gdk .color_parse (self .color .get_fill_color ()).green_float +
268- Gdk .color_parse (self .color .get_fill_color ()).blue_float ) / 3.0
269- if bright < 0.5 :
270- col = 'white'
278+ justification = Gtk .Justification .LEFT )
279+
280+ # Calculate brightness
281+ c = Gdk .RGBA ()
282+ if c .parse (fill_color ):
283+ bright = (c .red + c .green + c .blue ) / 3.0
284+ if bright < 0.5 :
285+ col = 'white'
286+ else :
287+ col = 'black'
271288 else :
272289 col = 'black'
290+
273291 tagcolor = buf .create_tag (foreground = col )
274292
275293 # Add label, equation and result
@@ -279,13 +297,22 @@ def create_history_object(self):
279297 eqnstr = '%s\n ' % str (self .equation )
280298 self .append_with_superscript_tags (buf , eqnstr , tagsmall )
281299
282- resstr = self .ml .format_number (self .result )
283- resstr = str (resstr ).rstrip ('0' ).rstrip ('.' ) \
284- if '.' in resstr else resstr
300+ try :
301+ resstr = self .ml .format_number (self .result )
302+ except Exception :
303+ resstr = str (self .result )
304+
305+ resstr = str (resstr )
306+ resstr = resstr .rstrip ('0' ).rstrip ('.' ) if '.' in resstr else resstr
307+
308+ if not resstr or resstr == "None" :
309+ resstr = "0"
310+
285311 if len (resstr ) > 30 :
286312 restag = tagsmall
287313 else :
288314 restag = tagbig
315+
289316 self .append_with_superscript_tags (buf , resstr , restag )
290317
291318 buf .apply_tag (tagcolor , buf .get_start_iter (), buf .get_end_iter ())
@@ -374,23 +401,28 @@ def __init__(self, handle):
374401 self .KEYMAP ['divide' ] = self .ml .div_sym
375402 self .KEYMAP ['equal' ] = self .ml .equ_sym
376403
377- self .clipboard = Gtk . Clipboard . get ( Gdk . SELECTION_CLIPBOARD )
404+ self .clipboard = Gdk . Display . get_default (). get_clipboard ( )
378405 self .select_reason = self .SELECT_SELECT
379406 self .buffer = ""
380407 self .showing_version = 0
381408 self .showing_error = False
382409 self .ans_inserted = False
383410 self .show_vars = False
384411
385- self .connect ("key_press_event" , self .keypress_cb )
386- self .connect ("destroy" , self .cleanup_cb )
387- self .color = sugar3 .profile .get_color ()
412+ key_controller = Gtk .EventControllerKey ()
413+ key_controller .connect ("key-pressed" , self .keypress_cb )
414+ self .add_controller (key_controller )
415+
416+ # self.connect("destroy", self.cleanup_cb) # handled by activity?
417+
418+ self .color = sugar4 .profile .get_color ()
388419
389420 self .layout = CalcLayout (self )
390421 self .label_entry = self .layout .label_entry
391422 self .text_entry = self .layout .text_entry
392423 self .last_eq_sig = None
393424 self .last_eqn_textview = None
425+ self .last_eqn_controller = None
394426
395427 self .reset ()
396428 self .layout .show_it ()
@@ -399,7 +431,7 @@ def __init__(self, handle):
399431
400432 self .parser .log_debug_info ()
401433
402- def ignore_key_cb (self , widget , event ):
434+ def ignore_key_cb (self , controller , keyval , keycode , state ):
403435 return True
404436
405437 def cleanup_cb (self , arg ):
@@ -427,14 +459,15 @@ def equation_pressed_cb(self, eqn):
427459 def set_last_equation (self , eqn ):
428460 """Set the 'last equation' TextView."""
429461
430- if self .last_eq_sig is not None :
431- self .layout .last_eq .disconnect (self .last_eq_sig )
432- self .last_eq_sig = None
462+ if self .last_eqn_controller is not None :
463+ self .layout .last_eq .remove_controller (self .last_eqn_controller )
464+ self .last_eqn_controller = None
433465
434466 if not isinstance (eqn .result , ParserError ):
435- self .last_eq_sig = self .layout .last_eq .connect (
436- 'button-press-event' ,
437- lambda a1 , a2 , e : self .equation_pressed_cb (e ), eqn )
467+ self .last_eqn_controller = Gtk .GestureClick .new ()
468+ self .last_eqn_controller .set_button (0 )
469+ self .last_eqn_controller .connect ('pressed' , lambda gesture , n_press , x , y : self .equation_pressed_cb (eqn ))
470+ self .layout .last_eq .add_controller (self .last_eqn_controller )
438471
439472 self .layout .last_eq .set_buffer (eqn .create_lasteq_textbuf ())
440473
@@ -490,8 +523,10 @@ def add_equation(self, eq, prepend=False, drawlasteq=False, tree=None):
490523
491524 own = (eq .owner == self .get_owner_id ())
492525 w = eq .create_history_object ()
493- w .connect ('button-press-event' , lambda w ,
494- e : self .equation_pressed_cb (eq ))
526+ click_gesture = Gtk .GestureClick .new ()
527+ click_gesture .set_button (0 )
528+ click_gesture .connect ('pressed' , lambda gesture , n_press , x , y : self .equation_pressed_cb (eq ))
529+ w .add_controller (click_gesture )
495530 if drawlasteq :
496531 self .set_last_equation (eq )
497532
@@ -551,7 +586,7 @@ def process(self):
551586 self .get_owner_id (), ml = self .ml )
552587 self .set_error_equation (eqn )
553588 else :
554- eqn = Equation (label , _n (s ), _n ( str ( res )) , self .color ,
589+ eqn = Equation (label , _n (s ), res , self .color ,
555590 self .get_owner_id (), ml = self .ml )
556591 self .add_equation (eqn , drawlasteq = True , tree = tree )
557592 self .send_message ("add_eq" , value = str (eqn ))
@@ -576,28 +611,38 @@ def create_var_textview(self, name, value):
576611 if name in reserved :
577612 return None
578613 w = Gtk .TextView ()
579- w .modify_base (
580- Gtk .StateType .NORMAL , Gdk .color_parse (self .color .get_fill_color ()))
581- w .modify_bg (
582- Gtk .StateType .NORMAL ,
583- Gdk .color_parse (self .color .get_stroke_color ()))
614+ fill_color = self .color .get_fill_color ()
615+ stroke_color = self .color .get_stroke_color ()
616+
617+ css_str = """
618+ textview {
619+ background-color: %s;
620+ color: %s;
621+ }
622+ textview text {
623+ background-color: %s;
624+ color: %s;
625+ }
626+ """ % (fill_color , stroke_color , fill_color , stroke_color )
627+ sugar_style .apply_css_to_widget (w , css_str )
628+
584629 w .set_wrap_mode (Gtk .WrapMode .WORD_CHAR )
585- w .set_border_window_size ( Gtk . TextWindowType . LEFT , 4 )
586- w .set_border_window_size ( Gtk . TextWindowType . RIGHT , 4 )
587- w .set_border_window_size ( Gtk . TextWindowType . TOP , 4 )
588- w .set_border_window_size ( Gtk . TextWindowType . BOTTOM , 4 )
630+ w .set_left_margin ( 4 )
631+ w .set_right_margin ( 4 )
632+ w .set_top_margin ( 4 )
633+ w .set_bottom_margin ( 4 )
589634 w .connect ('realize' , _textview_realize_cb )
590635 buf = w .get_buffer ()
591636
592- # TODO Fix for old Sugar 0.82 builds, red_float not available
593- bright = (
594- Gdk . color_parse ( self . color . get_fill_color ()). red_float +
595- Gdk . color_parse ( self . color . get_fill_color ()). green_float +
596- Gdk . color_parse ( self . color . get_fill_color ()). blue_float ) / 3.0
597- if bright < 0.5 :
598- col = Gdk . color_parse ( 'white' )
637+ c = Gdk . RGBA ()
638+ if c . parse ( fill_color ):
639+ bright = ( c . red + c . green + c . blue ) / 3.0
640+ if bright < 0.5 :
641+ col = 'white'
642+ else :
643+ col = 'black'
599644 else :
600- col = Gdk . color_parse ( 'black' )
645+ col = 'black'
601646
602647 tag = buf .create_tag (font = CalcLayout .FONT_SMALL_NARROW ,
603648 foreground = col )
@@ -792,67 +837,66 @@ def expand_selection(self, dir):
792837
793838 def text_copy (self ):
794839 if self .layout .graph_selected is not None :
795- self .clipboard .set_image (
796- self .layout .graph_selected .get_child ().get_pixbuf ())
797- self .layout .toggle_select_graph (self .layout .graph_selected )
840+ # TODO: Port image copying for GTK4 (requires Texture)
841+ pass
842+ # texture = Gdk.Texture.new_for_pixbuf(self.layout.graph_selected.get_child().get_pixbuf())
843+ # self.clipboard.set_texture(texture)
844+ # self.layout.toggle_select_graph(self.layout.graph_selected)
798845 else :
799846 str = self .text_entry .get_text ()
800847 sel = self .text_entry .get_selection_bounds ()
801848 # _logger.info('text_copy, sel: %r, str: %s', sel, str)
802849 if len (sel ) == 2 :
803850 (start , end ) = sel
804- self .clipboard .set_text (str [start :end ], - 1 )
851+ self .clipboard .set_text (str [start :end ])
805852
806853 def text_select_all (self ):
807854 end = self .text_entry .get_text_length ()
808855 self .text_entry .select_region (0 , end )
809856
810- def get_clipboard_text (self ):
811- text = self .clipboard .wait_for_text ()
812- if text is None :
813- return ""
814- else :
815- return text
816-
817857 def text_paste (self ):
818- self .button_pressed (self .TYPE_TEXT , self .get_clipboard_text ())
858+ self .clipboard .read_text_async (None , self ._on_paste_text_received , None )
859+
860+ def _on_paste_text_received (self , clipboard , result , user_data ):
861+ try :
862+ text = clipboard .read_text_finish (result )
863+ if text :
864+ self .button_pressed (self .TYPE_TEXT , text )
865+ except Exception as e :
866+ _logger .error ("Error pasting text: %s" , e )
819867
820868 def text_cut (self ):
821869 self .text_copy ()
822870 self .remove_character (1 )
823871
824- def keypress_cb (self , widget , event ):
825- if not self .text_entry .is_focus ():
826- return
827-
828- key = Gdk .keyval_name (event .keyval )
829- if event .hardware_keycode == 219 :
830- if (event .get_state () & Gdk .ModifierType .SHIFT_MASK ):
831- key = 'divide'
832- else :
833- key = 'multiply'
834- _logger .debug ('Key: %s (%r, %r)' , key ,
835- event .keyval , event .hardware_keycode )
836-
837- if event .get_state () & Gdk .ModifierType .CONTROL_MASK :
838- if key in self .CTRL_KEYMAP :
839- f = self .CTRL_KEYMAP [key ]
840- return f (self )
841- elif (event .get_state () & Gdk .ModifierType .SHIFT_MASK ) and \
842- key in self .SHIFT_KEYMAP :
843- f = self .SHIFT_KEYMAP [key ]
844- return f (self )
845- elif str (key ) in self .IDENTIFIER_CHARS :
846- self .button_pressed (self .TYPE_TEXT , key )
847- elif key in self .KEYMAP :
848- f = self .KEYMAP [key ]
849- if isinstance (f , str ) or \
850- isinstance (f , str ):
851- self .button_pressed (self .TYPE_TEXT , f )
872+ def keypress_cb (self , controller , keyval , keycode , state ):
873+ keyname = Gdk .keyval_name (keyval )
874+
875+ is_ctrl = (state & Gdk .ModifierType .CONTROL_MASK )
876+ is_shift = (state & Gdk .ModifierType .SHIFT_MASK ) and not is_ctrl
877+
878+ if is_ctrl :
879+ if keyname in self .CTRL_KEYMAP :
880+ self .CTRL_KEYMAP [keyname ](self )
881+ return True
882+ elif is_shift :
883+ if keyname in self .SHIFT_KEYMAP :
884+ self .SHIFT_KEYMAP [keyname ](self )
885+ return True
886+
887+ if "KP_" in keyname :
888+ if keyname == "KP_Enter" :
889+ keyname = "Return"
890+
891+ if keyname in self .KEYMAP :
892+ action = self .KEYMAP [keyname ]
893+ if callable (action ):
894+ action (self )
852895 else :
853- return f (self )
896+ self .button_pressed (self .TYPE_TEXT , action )
897+ return True
854898
855- return True
899+ return False
856900
857901 def get_older (self ):
858902 self .showing_version = max (0 , self .showing_version - 1 )
@@ -970,14 +1014,3 @@ def format_insert_ans(self):
9701014 return self .ml .format_number (ans )
9711015 else :
9721016 return ''
973-
974-
975- def main ():
976- win = Gtk .Window (Gtk .WindowType .TOPLEVEL )
977- Calculate (win )
978- Gtk .main ()
979- return 0
980-
981-
982- if __name__ == "__main__" :
983- main ()
0 commit comments