@@ -132,10 +132,7 @@ def on_model_updated(self, model, position, removed, added, data=None):
132132 def _update_selected_row (self , data = None ):
133133 self .input_sources_list .handler_block (self .source_activate_handler )
134134
135- for row in self .input_sources_list .get_children ():
136- source = row .get_child ().input_source
137- if source .active :
138- self .input_sources_list .select_row (row )
135+ self .input_sources_list .unselect_all ()
139136
140137 self .input_sources_list .handler_unblock (self .source_activate_handler )
141138 self .update_widgets ()
@@ -178,8 +175,9 @@ def on_test_layout_clicked(self, button, data=None):
178175
179176 def on_engine_config_clicked (self , button , data = None ):
180177 source = self ._get_selected_source ()
181-
182- subprocess .Popen ([source .preferences ], shell = True )
178+ dialog = IBusConfigDialog (source , self .current_input_sources_model .input_source_settings )
179+ dialog .run ()
180+ dialog .destroy ()
183181
184182 def update_widgets (self ):
185183 # Don't allow removal of last remaining layout
@@ -189,7 +187,8 @@ def update_widgets(self):
189187 source = self ._get_selected_source ()
190188 if source is not None :
191189 self .test_layout_button .set_sensitive (source .type == "xkb" )
192- self .engine_config_button .set_sensitive (source .type == "ibus" and source .preferences != '' )
190+ # Enable Configure button for all IBus sources (not just those with preferences)
191+ self .engine_config_button .set_sensitive (source .type == "ibus" )
193192 index = self .current_input_sources_model .get_item_index (source )
194193 self .move_layout_up_button .set_sensitive (index > 0 )
195194 self .move_layout_down_button .set_sensitive (index < self .current_input_sources_model .get_n_items () - 1 )
@@ -201,6 +200,142 @@ def update_widgets(self):
201200 self .move_layout_down_button .set_sensitive (False )
202201 self .remove_layout_button .set_sensitive (False )
203202
203+
204+ class IBusConfigDialog ():
205+ def __init__ (self , source , settings ):
206+ self .source = source
207+ self .settings = settings
208+ self .xkb_info = CinnamonDesktop .XkbInfo .new_with_extras ()
209+
210+ # Check if this engine uses "default" layout or has a specific one
211+ self .engine_layout = self ._get_engine_layout ()
212+ self .allows_override = (self .engine_layout == "default" )
213+
214+ builder = Gtk .Builder ()
215+ builder .set_translation_domain ('cinnamon' )
216+ builder .add_from_file ("/usr/share/cinnamon/cinnamon-settings/bin/input-sources-list.ui" )
217+
218+ self .dialog = builder .get_object ("ibus_config_dialog" )
219+
220+ # Set up name label
221+ name_label = builder .get_object ("ibus_config_name_label" )
222+ name_label .set_text (source .display_name )
223+
224+ # Set up explanation label based on whether override is allowed
225+ explanation_label = builder .get_object ("ibus_config_explanation_label" )
226+ if not self .allows_override :
227+ layout_display = self ._get_engine_layout_display_name ()
228+ explanation_label .set_text (
229+ _ ("This input method requires the \" %s\" keyboard layout to function correctly. "
230+ "The layout is set automatically when you switch to this input method." ) % layout_display
231+ )
232+
233+ # Set up layout label
234+ self .layout_label = builder .get_object ("ibus_config_layout_label" )
235+
236+ # Set up buttons
237+ close_button = builder .get_object ("ibus_config_close_button" )
238+ close_button .connect ("clicked" , self .on_close_clicked )
239+
240+ self .change_layout_button = builder .get_object ("ibus_config_change_layout_button" )
241+ self .change_layout_button .connect ("clicked" , self .on_change_layout_clicked )
242+ self .change_layout_button .set_sensitive (self .allows_override )
243+
244+ self .clear_override_button = builder .get_object ("ibus_config_clear_override_button" )
245+ self .clear_override_button .connect ("clicked" , self .on_clear_override_clicked )
246+
247+ engine_settings_button = builder .get_object ("ibus_config_engine_settings_button" )
248+ if source .preferences :
249+ engine_settings_button .connect ("clicked" , self .on_engine_settings_clicked )
250+ else :
251+ engine_settings_button .set_visible (False )
252+
253+ self .update_layout_display ()
254+ self .dialog .show_all ()
255+
256+ def _get_engine_layout (self ):
257+ ibus = IBus .Bus .new ()
258+ if ibus .is_connected ():
259+ engines = ibus .get_engines_by_names ([self .source .id ])
260+ if engines :
261+ return engines [0 ].get_layout () or "default"
262+ return "default"
263+
264+ def _get_engine_layout_display_name (self ):
265+ if not self .engine_layout or self .engine_layout == "default" :
266+ return _ ("Default" )
267+ got , display_name , short_name , layout , variant = self .xkb_info .get_layout_info (self .engine_layout )
268+ if got :
269+ return f"{ display_name } ({ self .engine_layout } )"
270+ return self .engine_layout
271+
272+ def run (self ):
273+ return self .dialog .run ()
274+
275+ def destroy (self ):
276+ self .dialog .destroy ()
277+
278+ def on_close_clicked (self , button , data = None ):
279+ self .dialog .response (Gtk .ResponseType .CLOSE )
280+
281+ def get_current_override (self ):
282+ source_layouts = self .settings .get_value ("source-layouts" ).unpack ()
283+ return source_layouts .get (self .source .id , None )
284+
285+ def get_layout_display_name (self , layout_id ):
286+ if layout_id is None :
287+ return None
288+ got , display_name , short_name , layout , variant = self .xkb_info .get_layout_info (layout_id )
289+ if got :
290+ return f"{ display_name } ({ layout_id } )"
291+ return layout_id
292+
293+ def update_layout_display (self ):
294+ if not self .allows_override :
295+ # Engine has a fixed layout requirement
296+ self .layout_label .set_text (self ._get_engine_layout_display_name ())
297+ self .clear_override_button .set_sensitive (False )
298+ return
299+
300+ override = self .get_current_override ()
301+ if override :
302+ display_name = self .get_layout_display_name (override )
303+ self .layout_label .set_text (display_name )
304+ self .clear_override_button .set_sensitive (True )
305+ else :
306+ self .layout_label .set_text (_ ("Default" ))
307+ self .clear_override_button .set_sensitive (False )
308+
309+ def on_change_layout_clicked (self , button , data = None ):
310+ # Show the layout picker in XKB-only mode
311+ add_dialog = AddKeyboardLayout .AddKeyboardLayoutDialog ([], xkb_only = True )
312+ add_dialog .dialog .set_transient_for (self .dialog )
313+ add_dialog .dialog .show_all ()
314+ ret = add_dialog .dialog .run ()
315+ if ret == Gtk .ResponseType .OK :
316+ layout_type , layout_id = add_dialog .response
317+ self .set_layout_override (layout_id )
318+ add_dialog .dialog .destroy ()
319+
320+ def on_clear_override_clicked (self , button , data = None ):
321+ self .clear_layout_override ()
322+
323+ def on_engine_settings_clicked (self , button , data = None ):
324+ subprocess .Popen ([self .source .preferences ], shell = True )
325+
326+ def set_layout_override (self , layout_id ):
327+ source_layouts = self .settings .get_value ("source-layouts" ).unpack ()
328+ source_layouts [self .source .id ] = layout_id
329+ self .settings .set_value ("source-layouts" , GLib .Variant ("a{ss}" , source_layouts ))
330+ self .update_layout_display ()
331+
332+ def clear_layout_override (self ):
333+ source_layouts = self .settings .get_value ("source-layouts" ).unpack ()
334+ if self .source .id in source_layouts :
335+ del source_layouts [self .source .id ]
336+ self .settings .set_value ("source-layouts" , GLib .Variant ("a{ss}" , source_layouts ))
337+ self .update_layout_display ()
338+
204339class LayoutIcon (Gtk .Overlay ):
205340 def __init__ (self , file , dupe_id ):
206341 Gtk .Overlay .__init__ (self )
@@ -267,6 +402,7 @@ def __init__(self):
267402 self .interface_settings = Gio .Settings (schema_id = "org.cinnamon.desktop.interface" )
268403 self .interface_settings .connect ("changed" , self .on_interface_settings_changed )
269404 self .input_source_settings = Gio .Settings (schema_id = "org.cinnamon.desktop.input-sources" )
405+ self .input_source_settings .connect ("changed::source-layouts" , self .on_source_layouts_changed )
270406
271407 try :
272408 Gio .DBusProxy .new_for_bus (Gio .BusType .SESSION , Gio .DBusProxyFlags .NONE , None ,
@@ -293,6 +429,16 @@ def live(self):
293429 return False
294430 return True
295431
432+ def _get_layout_override (self , engine_id ):
433+ source_layouts = self .input_source_settings .get_value ("source-layouts" ).unpack ()
434+ return source_layouts .get (engine_id , None )
435+
436+ def _get_layout_display_name (self , layout_id ):
437+ got , display_name , short_name , layout , variant = self .xkb_info .get_layout_info (layout_id )
438+ if got :
439+ return display_name
440+ return layout_id
441+
296442 def _on_ibus_connected (self , ibus , data = None ):
297443 if self ._proxy is None :
298444 self .refresh_input_source_list ()
@@ -324,6 +470,9 @@ def on_interface_settings_changed(self, settings, key, data=None):
324470 if key .startswith ("keyboard-layout-" ):
325471 self .refresh_input_source_list ()
326472
473+ def on_source_layouts_changed (self , settings , key , data = None ):
474+ self .refresh_input_source_list ()
475+
327476 def refresh_input_source_list (self ):
328477 if self .live :
329478 layouts = self ._proxy .GetInputSources ()
@@ -377,7 +526,18 @@ def show_add_layout_dialog(self):
377526
378527 def create_row (self , source , data = None ):
379528 row = Gtk .Box (orientation = Gtk .Orientation .HORIZONTAL , spacing = 6 )
380- markup = f"<b>{ source .display_name } </b>"
529+
530+ # Build display name, including layout override for IBus sources
531+ display_name = GLib .markup_escape_text (source .display_name )
532+ if source .type == "ibus" :
533+ layout_override = self ._get_layout_override (source .id )
534+ if layout_override :
535+ layout_display = GLib .markup_escape_text (self ._get_layout_display_name (layout_override ))
536+ markup = f"<b>{ display_name } </b> <small>| { layout_display } </small>"
537+ else :
538+ markup = f"<b>{ display_name } </b>"
539+ else :
540+ markup = f"<b>{ display_name } </b>"
381541 label = Gtk .Label (label = markup , xalign = 0.0 , use_markup = True , margin_start = 4 )
382542 row .pack_start (label , True , True , 0 )
383543
0 commit comments