2 Commits

Author SHA1 Message Date
davide 4c0edde4f7 feat(ui): mobile polish — tab bar icons, server overlay, status text
Tab bar: replace plain Header strings with icon+label StackPanels
(≡ ↑ ↓ ⊙ ⊕); style TabStrip with UniformGrid so all five tabs share
the full width equally; center content and add touch padding. Icons
are visible only on mobile (IsMobile binding); desktop keeps text only.

Server settings overlay: dual layout for host/port/TLS input (vertical
on mobile: host full-width, port+TLS side by side) and for action
buttons (stacked full-width on mobile). Known-server list item shows
host on one line and tcp/ssl ports on the line below on mobile.

Connection status bar: simplify to "connesso" / "non connesso" —
remove the "a host:port" suffix from the connected state and
consolidate all disconnected/error/cert-changed states to conn.none.
2026-06-14 22:05:30 +02:00
davide cfc48ff86f feat(ui): responsive layout for portrait mobile (Android)
Add IsMobile property and BoolToTabPlacementConverter; wire
TabStripPlacement to move tabs to the bottom on mobile (standard
Android pattern). Hide the desktop menu bar on mobile and expose
Settings/Help via a compact header row instead.

Replace fixed-width multi-column grids with dual desktop/mobile
templates in History, Addresses and Contacts lists; adapt Send,
Receive and the add-contact form for narrow screens. Remove hard-coded
Width from all five overlays (560–640 px) in favour of MaxWidth +
Margin="16" so they never overflow a 360 px screen; add ScrollViewer
to Address info, Server settings and Settings overlays.

Desktop layout is unchanged: all differences are gated on the
IsMobile/IsDesktop bool, which is a platform constant (false on desktop).
2026-06-14 19:54:11 +02:00
5 changed files with 301 additions and 65 deletions
+2 -2
View File
@@ -52,7 +52,7 @@ public sealed class Loc
["menu.net.resetcerts"] = ["Reset certificati SSL", "Reset SSL certificates", "Restablecer certificados SSL", "Réinitialiser les certificats SSL", "Redefinir certificados SSL", "SSL-Zertifikate zurücksetzen"], ["menu.net.resetcerts"] = ["Reset certificati SSL", "Reset SSL certificates", "Restablecer certificados SSL", "Réinitialiser les certificats SSL", "Redefinir certificados SSL", "SSL-Zertifikate zurücksetzen"],
["menu.settings"] = ["_Impostazioni", "_Settings", "_Configuración", "_Paramètres", "_Configurações", "_Einstellungen"], ["menu.settings"] = ["_Impostazioni", "_Settings", "_Configuración", "_Paramètres", "_Configurações", "_Einstellungen"],
["menu.help"] = ["_Help", "_Help", "_Ayuda", "_Aide", "_Ajuda", "_Hilfe"], ["menu.help"] = ["_Help", "_Help", "_Ayuda", "_Aide", "_Ajuda", "_Hilfe"],
["help.title"] = ["Informazioni sul software", "About this software", "Información del software", "À propos du logiciel", "Sobre o software", "Über die Software"], ["help.title"] = ["Informazioni", "About", "Información", "À propos", "Sobre", "Über"],
["help.info"] = [ ["help.info"] = [
"Wallet SPV per la criptovaluta Palladium (PLM).", "Wallet SPV per la criptovaluta Palladium (PLM).",
"SPV wallet for the Palladium (PLM) cryptocurrency.", "SPV wallet for the Palladium (PLM) cryptocurrency.",
@@ -191,7 +191,7 @@ public sealed class Loc
["conn.reconnecting"] = ["riconnessione…", "reconnecting…", "reconectando…", "reconnexion…", "reconectando…", "Verbindung wird wiederhergestellt…"], ["conn.reconnecting"] = ["riconnessione…", "reconnecting…", "reconectando…", "reconnexion…", "reconectando…", "Verbindung wird wiederhergestellt…"],
["conn.error"] = ["errore di connessione", "connection error", "error de conexión", "erreur de connexion", "erro de conexão", "Verbindungsfehler"], ["conn.error"] = ["errore di connessione", "connection error", "error de conexión", "erreur de connexion", "erro de conexão", "Verbindungsfehler"],
["conn.certchanged"] = ["certificato cambiato", "certificate changed", "certificado cambiado", "certificat modifié", "certificado alterado", "Zertifikat geändert"], ["conn.certchanged"] = ["certificato cambiato", "certificate changed", "certificado cambiado", "certificat modifié", "certificado alterado", "Zertifikat geändert"],
["conn.connectedto"] = ["connesso a", "connected to", "conectado a", "connecté à", "conectado a", "verbunden mit"], ["conn.connectedto"] = ["connesso", "connected", "conectado", "connecté", "conectado", "verbunden"],
["conn.connectingto"] = ["connessione a", "connecting to", "conectando a", "connexion à", "conectando a", "Verbindung zu"], ["conn.connectingto"] = ["connessione a", "connecting to", "conectando a", "connexion à", "conectando a", "Verbindung zu"],
// Messaggi di stato principali // Messaggi di stato principali
@@ -0,0 +1,21 @@
using System;
using System.Globalization;
using Avalonia.Controls;
using Avalonia.Data.Converters;
namespace PalladiumWallet.App.ViewModels;
/// <summary>
/// true (mobile) → Dock.Bottom — tab strip in basso, standard Android.
/// false (desktop) → Dock.Top — comportamento predefinito Avalonia.
/// </summary>
public sealed class BoolToTabPlacementConverter : IValueConverter
{
public static readonly BoolToTabPlacementConverter Instance = new();
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
value is true ? Dock.Bottom : Dock.Top;
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) =>
throw new NotSupportedException();
}
@@ -144,11 +144,11 @@ public partial class MainWindowViewModel
_client.Disconnected += _ => Dispatcher.UIThread.Post(() => _client.Disconnected += _ => Dispatcher.UIThread.Post(() =>
{ {
IsConnected = false; IsConnected = false;
ConnectionStatus = Loc.Tr("conn.disconnected"); ConnectionStatus = Loc.Tr("conn.none");
}); });
IsConnected = true; IsConnected = true;
_autoReconnect = true; _autoReconnect = true;
ConnectionStatus = $"{Loc.Tr("conn.connectedto")} {host}:{port}{(UseSsl ? " (TLS)" : "")}"; ConnectionStatus = Loc.Tr("conn.connectedto");
} }
if (_account is null || _doc is null) if (_account is null || _doc is null)
@@ -186,13 +186,13 @@ public partial class MainWindowViewModel
catch (CertificatePinMismatchException ex) catch (CertificatePinMismatchException ex)
{ {
IsConnected = false; IsConnected = false;
ConnectionStatus = Loc.Tr("conn.certchanged"); ConnectionStatus = Loc.Tr("conn.none");
StatusMessage = ex.Message; StatusMessage = ex.Message;
} }
catch (Exception ex) catch (Exception ex)
{ {
IsConnected = _client?.IsConnected == true; IsConnected = _client?.IsConnected == true;
ConnectionStatus = IsConnected ? ConnectionStatus : Loc.Tr("conn.error"); ConnectionStatus = IsConnected ? ConnectionStatus : Loc.Tr("conn.none");
StatusMessage = $"Errore: {ex.Message}"; StatusMessage = $"Errore: {ex.Message}";
} }
finally finally
@@ -94,6 +94,8 @@ public partial class MainWindowViewModel : ViewModelBase
/// <summary>true su desktop; false su Android/iOS — nasconde le funzioni legate al filesystem libero.</summary> /// <summary>true su desktop; false su Android/iOS — nasconde le funzioni legate al filesystem libero.</summary>
public bool IsDesktop => !OperatingSystem.IsAndroid() && !OperatingSystem.IsIOS(); public bool IsDesktop => !OperatingSystem.IsAndroid() && !OperatingSystem.IsIOS();
public bool IsMobile => !IsDesktop;
public string UnitLabel => _config.Unit; public string UnitLabel => _config.Unit;
public AppConfig CurrentConfig => _config; public AppConfig CurrentConfig => _config;
+244 -31
View File
@@ -15,7 +15,7 @@
<Grid RowDefinitions="Auto,*,Auto"> <Grid RowDefinitions="Auto,*,Auto">
<!-- ============ MENU (stile Electrum) ============ --> <!-- ============ MENU (stile Electrum) ============ -->
<Menu Grid.Row="0"> <Menu Grid.Row="0" IsVisible="{Binding IsDesktop}">
<MenuItem Header="{Binding Loc[menu.file]}"> <MenuItem Header="{Binding Loc[menu.file]}">
<MenuItem Header="{Binding Loc[menu.file.new]}" Command="{Binding NewWalletCommand}"/> <MenuItem Header="{Binding Loc[menu.file.new]}" Command="{Binding NewWalletCommand}"/>
<MenuItem Header="{Binding Loc[menu.file.open]}" Click="OnOpenWalletFileClick" <MenuItem Header="{Binding Loc[menu.file.open]}" Click="OnOpenWalletFileClick"
@@ -30,6 +30,14 @@
Command="{Binding OpenHelpCommand}"/> Command="{Binding OpenHelpCommand}"/>
</Menu> </Menu>
<!-- Pulsanti Settings/Help mobile-only (il menu è nascosto su mobile) -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="8"
HorizontalAlignment="Right" Margin="8,4"
IsVisible="{Binding IsMobile}">
<Button Content="{Binding Loc[menu.settings]}" Command="{Binding OpenSettingsCommand}"/>
<Button Content="{Binding Loc[menu.help]}" Command="{Binding OpenHelpCommand}"/>
</StackPanel>
<!-- ============ WIZARD DI SETUP (§15): un passo alla volta ============ --> <!-- ============ WIZARD DI SETUP (§15): un passo alla volta ============ -->
<ScrollViewer Grid.Row="1" IsVisible="{Binding IsSetupVisible}"> <ScrollViewer Grid.Row="1" IsVisible="{Binding IsSetupVisible}">
<StackPanel MaxWidth="560" Margin="24,40" Spacing="18" <StackPanel MaxWidth="560" Margin="24,40" Spacing="18"
@@ -188,18 +196,48 @@
</StackPanel> </StackPanel>
<!-- Tab: Storico / Invia / Ricevi / Indirizzi / Contatti --> <!-- Tab: Storico / Invia / Ricevi / Indirizzi / Contatti -->
<TabControl Grid.Row="1"> <TabControl Grid.Row="1"
TabStripPlacement="{Binding IsMobile, Converter={x:Static vm:BoolToTabPlacementConverter.Instance}}">
<TabControl.Styles>
<!-- Tab equamente distribuiti su tutta la larghezza -->
<Style Selector="TabStrip">
<Setter Property="ItemsPanel">
<ItemsPanelTemplate>
<UniformGrid Rows="1"/>
</ItemsPanelTemplate>
</Setter>
</Style>
<Style Selector="TabItem">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="6,8"/>
</Style>
</TabControl.Styles>
<!-- 1. Storico --> <!-- 1. Storico -->
<TabItem Header="{Binding Loc[tab.history]}"> <TabItem>
<TabItem.Header>
<StackPanel Spacing="2" HorizontalAlignment="Center">
<TextBlock Text="≡" FontSize="19" HorizontalAlignment="Center"
IsVisible="{Binding IsMobile}"/>
<TextBlock Text="{Binding Loc[tab.history]}" FontSize="12"
HorizontalAlignment="Center"/>
</StackPanel>
</TabItem.Header>
<Grid RowDefinitions="Auto,*" Margin="4"> <Grid RowDefinitions="Auto,*" Margin="4">
<TextBlock Grid.Row="0" Text="{Binding Loc[history.hint]}" <TextBlock Grid.Row="0" Text="{Binding Loc[history.hint]}"
IsVisible="{Binding IsDesktop}"
Foreground="Gray" FontSize="11" Margin="6,2"/> Foreground="Gray" FontSize="11" Margin="6,2"/>
<ListBox Grid.Row="1" ItemsSource="{Binding History}" <ListBox Grid.Row="1" ItemsSource="{Binding History}"
DoubleTapped="OnHistoryRowDoubleTapped"> DoubleTapped="OnHistoryRowDoubleTapped">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:HistoryRow"> <DataTemplate x:DataType="vm:HistoryRow">
<Grid ColumnDefinitions="90,160,*,70" Cursor="Hand"> <Panel Cursor="Hand">
<!-- Desktop: 4 colonne fisse -->
<Grid ColumnDefinitions="90,160,*,70"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Conferma}" Foreground="Gray"/> <TextBlock Grid.Column="0" Text="{Binding Conferma}" Foreground="Gray"/>
<TextBlock Grid.Column="1" Text="{Binding Importo}" FontFamily="monospace"/> <TextBlock Grid.Column="1" Text="{Binding Importo}" FontFamily="monospace"/>
<TextBlock Grid.Column="2" Text="{Binding Txid}" <TextBlock Grid.Column="2" Text="{Binding Txid}"
@@ -207,6 +245,20 @@
TextTrimming="CharacterEllipsis"/> TextTrimming="CharacterEllipsis"/>
<TextBlock Grid.Column="3" Text="{Binding Verificata}" Foreground="Green"/> <TextBlock Grid.Column="3" Text="{Binding Verificata}" Foreground="Green"/>
</Grid> </Grid>
<!-- Mobile: card verticale -->
<StackPanel Spacing="2"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsMobile}">
<Grid ColumnDefinitions="*,Auto">
<TextBlock Grid.Column="0" Text="{Binding Importo}"
FontFamily="monospace" FontWeight="SemiBold"/>
<TextBlock Grid.Column="1" Text="{Binding Verificata}"
Foreground="Green" FontSize="11"/>
</Grid>
<TextBlock Text="{Binding Conferma}" Foreground="Gray" FontSize="11"/>
<TextBlock Text="{Binding Txid}" FontFamily="monospace" FontSize="11"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</Panel>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
@@ -214,15 +266,22 @@
</TabItem> </TabItem>
<!-- 2. Invia --> <!-- 2. Invia -->
<TabItem Header="{Binding Loc[tab.send]}"> <TabItem>
<TabItem.Header>
<StackPanel Spacing="2" HorizontalAlignment="Center">
<TextBlock Text="↑" FontSize="19" HorizontalAlignment="Center"
IsVisible="{Binding IsMobile}"/>
<TextBlock Text="{Binding Loc[tab.send]}" FontSize="12"
HorizontalAlignment="Center"/>
</StackPanel>
</TabItem.Header>
<StackPanel Spacing="10" Margin="8" MaxWidth="640" HorizontalAlignment="Left"> <StackPanel Spacing="10" Margin="8" MaxWidth="640" HorizontalAlignment="Left">
<!-- Contatto rapido --> <!-- Contatto rapido -->
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding Loc[send.from.contact]}" VerticalAlignment="Center"/> <TextBlock Text="{Binding Loc[send.from.contact]}" VerticalAlignment="Center"/>
<ComboBox ItemsSource="{Binding Contacts}" <ComboBox ItemsSource="{Binding Contacts}"
SelectedItem="{Binding SendToContact}" SelectedItem="{Binding SendToContact}"
PlaceholderText="{Binding Loc[send.contact.hint]}" PlaceholderText="{Binding Loc[send.contact.hint]}">
MinWidth="220">
<ComboBox.ItemTemplate> <ComboBox.ItemTemplate>
<DataTemplate x:DataType="vm:ContactEntry"> <DataTemplate x:DataType="vm:ContactEntry">
<TextBlock Text="{Binding Name}"/> <TextBlock Text="{Binding Name}"/>
@@ -232,7 +291,8 @@
</StackPanel> </StackPanel>
<TextBox PlaceholderText="{Binding Loc[send.to]}" Text="{Binding SendTo}" <TextBox PlaceholderText="{Binding Loc[send.to]}" Text="{Binding SendTo}"
FontFamily="monospace"/> FontFamily="monospace"/>
<Grid ColumnDefinitions="*,Auto,Auto,Auto"> <!-- Desktop: riga amount + fee in 4 colonne -->
<Grid ColumnDefinitions="*,Auto,Auto,Auto" IsVisible="{Binding IsDesktop}">
<TextBox Grid.Column="0" PlaceholderText="{Binding Loc[send.amount]}" <TextBox Grid.Column="0" PlaceholderText="{Binding Loc[send.amount]}"
Text="{Binding SendAmount}" IsEnabled="{Binding !SendAll}"/> Text="{Binding SendAmount}" IsEnabled="{Binding !SendAll}"/>
<TextBlock Grid.Column="1" Text="{Binding UnitLabel}" <TextBlock Grid.Column="1" Text="{Binding UnitLabel}"
@@ -244,6 +304,21 @@
<TextBox Text="{Binding SendFeeRate}" MinWidth="60"/> <TextBox Text="{Binding SendFeeRate}" MinWidth="60"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
<!-- Mobile: amount e fee su righe separate -->
<StackPanel Spacing="6" IsVisible="{Binding IsMobile}">
<Grid ColumnDefinitions="*,Auto,Auto">
<TextBox Grid.Column="0" PlaceholderText="{Binding Loc[send.amount]}"
Text="{Binding SendAmount}" IsEnabled="{Binding !SendAll}"/>
<TextBlock Grid.Column="1" Text="{Binding UnitLabel}"
VerticalAlignment="Center" Margin="6,0" Foreground="Gray"/>
<CheckBox Grid.Column="2" Content="{Binding Loc[send.all]}" Margin="6,0"
IsChecked="{Binding SendAll}"/>
</Grid>
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock Text="{Binding Loc[send.feerate]}" VerticalAlignment="Center"/>
<TextBox Text="{Binding SendFeeRate}" MinWidth="60"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="10"> <StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="{Binding Loc[send.prepare]}" Command="{Binding PrepareSendCommand}"/> <Button Content="{Binding Loc[send.prepare]}" Command="{Binding PrepareSendCommand}"/>
<Button Content="{Binding Loc[send.confirm]}" Classes="accent" <Button Content="{Binding Loc[send.confirm]}" Classes="accent"
@@ -256,10 +331,19 @@
</TabItem> </TabItem>
<!-- 3. Ricevi --> <!-- 3. Ricevi -->
<TabItem Header="{Binding Loc[tab.receive]}"> <TabItem>
<TabItem.Header>
<StackPanel Spacing="2" HorizontalAlignment="Center">
<TextBlock Text="↓" FontSize="19" HorizontalAlignment="Center"
IsVisible="{Binding IsMobile}"/>
<TextBlock Text="{Binding Loc[tab.receive]}" FontSize="12"
HorizontalAlignment="Center"/>
</StackPanel>
</TabItem.Header>
<StackPanel Spacing="10" Margin="8"> <StackPanel Spacing="10" Margin="8">
<TextBlock Text="{Binding Loc[receive.next]}"/> <TextBlock Text="{Binding Loc[receive.next]}"/>
<StackPanel Orientation="Horizontal" Spacing="8"> <!-- Desktop: indirizzo + copia su una riga -->
<StackPanel Orientation="Horizontal" Spacing="8" IsVisible="{Binding IsDesktop}">
<SelectableTextBlock Text="{Binding ReceiveAddress}" <SelectableTextBlock Text="{Binding ReceiveAddress}"
FontFamily="monospace" FontSize="16" FontFamily="monospace" FontSize="16"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
@@ -267,8 +351,18 @@
Click="OnCopyReceiveAddressClick" Click="OnCopyReceiveAddressClick"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</StackPanel> </StackPanel>
<!-- Mobile: indirizzo su riga intera + copia sotto -->
<StackPanel Spacing="6" IsVisible="{Binding IsMobile}">
<SelectableTextBlock Text="{Binding ReceiveAddress}"
FontFamily="monospace" FontSize="13"
TextWrapping="Wrap"/>
<Button Content="{Binding Loc[receive.copy]}"
Click="OnCopyReceiveAddressClick"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"/>
</StackPanel>
<Border Background="White" Padding="12" CornerRadius="6" <Border Background="White" Padding="12" CornerRadius="6"
HorizontalAlignment="Left" HorizontalAlignment="Center"
IsVisible="{Binding ReceiveQr, Converter={x:Static ObjectConverters.IsNotNull}}"> IsVisible="{Binding ReceiveQr, Converter={x:Static ObjectConverters.IsNotNull}}">
<Image Source="{Binding ReceiveQr}" Width="220" Height="220" <Image Source="{Binding ReceiveQr}" Width="220" Height="220"
RenderOptions.BitmapInterpolationMode="None"/> RenderOptions.BitmapInterpolationMode="None"/>
@@ -279,9 +373,19 @@
</TabItem> </TabItem>
<!-- 4. Indirizzi --> <!-- 4. Indirizzi -->
<TabItem Header="{Binding Loc[tab.addresses]}"> <TabItem>
<TabItem.Header>
<StackPanel Spacing="2" HorizontalAlignment="Center">
<TextBlock Text="⊙" FontSize="19" HorizontalAlignment="Center"
IsVisible="{Binding IsMobile}"/>
<TextBlock Text="{Binding Loc[tab.addresses]}" FontSize="12"
HorizontalAlignment="Center"/>
</StackPanel>
</TabItem.Header>
<Grid RowDefinitions="Auto,*" Margin="4"> <Grid RowDefinitions="Auto,*" Margin="4">
<Grid Grid.Row="0" ColumnDefinitions="90,60,*,140,60" Margin="12,4"> <!-- Intestazione colonne — solo desktop -->
<Grid Grid.Row="0" ColumnDefinitions="90,60,*,140,60" Margin="12,4"
IsVisible="{Binding IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Loc[addr.type]}" FontWeight="Bold"/> <TextBlock Grid.Column="0" Text="{Binding Loc[addr.type]}" FontWeight="Bold"/>
<TextBlock Grid.Column="1" Text="{Binding Loc[addr.index]}" FontWeight="Bold"/> <TextBlock Grid.Column="1" Text="{Binding Loc[addr.index]}" FontWeight="Bold"/>
<TextBlock Grid.Column="2" Text="{Binding Loc[addr.address]}" FontWeight="Bold"/> <TextBlock Grid.Column="2" Text="{Binding Loc[addr.address]}" FontWeight="Bold"/>
@@ -295,7 +399,10 @@
PointerPressed="OnAddressListPointerPressed"> PointerPressed="OnAddressListPointerPressed">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:AddressRow"> <DataTemplate x:DataType="vm:AddressRow">
<Grid ColumnDefinitions="90,60,*,140,60"> <Panel>
<!-- Desktop: 5 colonne fisse -->
<Grid ColumnDefinitions="90,60,*,140,60"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Tipo}" Foreground="Gray"/> <TextBlock Grid.Column="0" Text="{Binding Tipo}" Foreground="Gray"/>
<TextBlock Grid.Column="1" Text="{Binding Indice}" Foreground="Gray"/> <TextBlock Grid.Column="1" Text="{Binding Indice}" Foreground="Gray"/>
<TextBlock Grid.Column="2" Text="{Binding Indirizzo}" <TextBlock Grid.Column="2" Text="{Binding Indirizzo}"
@@ -303,6 +410,20 @@
<TextBlock Grid.Column="3" Text="{Binding Saldo}" FontFamily="monospace"/> <TextBlock Grid.Column="3" Text="{Binding Saldo}" FontFamily="monospace"/>
<TextBlock Grid.Column="4" Text="{Binding NumTx}" Foreground="Gray"/> <TextBlock Grid.Column="4" Text="{Binding NumTx}" Foreground="Gray"/>
</Grid> </Grid>
<!-- Mobile: card verticale -->
<StackPanel Spacing="1" Margin="0,3"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsMobile}">
<TextBlock Text="{Binding Indirizzo}"
FontFamily="monospace" FontSize="12"
TextTrimming="CharacterEllipsis"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{Binding Tipo}" Foreground="Gray" FontSize="11"/>
<TextBlock Text="{Binding Indice}" Foreground="Gray" FontSize="11"/>
<TextBlock Text="{Binding Saldo}" FontFamily="monospace" FontSize="11"/>
<TextBlock Text="{Binding NumTx}" Foreground="Gray" FontSize="11"/>
</StackPanel>
</StackPanel>
</Panel>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
@@ -310,10 +431,19 @@
</TabItem> </TabItem>
<!-- 5. Contatti --> <!-- 5. Contatti -->
<TabItem Header="{Binding Loc[tab.contacts]}"> <TabItem>
<TabItem.Header>
<StackPanel Spacing="2" HorizontalAlignment="Center">
<TextBlock Text="⊕" FontSize="19" HorizontalAlignment="Center"
IsVisible="{Binding IsMobile}"/>
<TextBlock Text="{Binding Loc[tab.contacts]}" FontSize="12"
HorizontalAlignment="Center"/>
</StackPanel>
</TabItem.Header>
<Grid RowDefinitions="Auto,*,Auto,Auto" Margin="4"> <Grid RowDefinitions="Auto,*,Auto,Auto" Margin="4">
<!-- Intestazioni colonne --> <!-- Intestazioni colonne — solo desktop -->
<Grid Grid.Row="0" ColumnDefinitions="180,*" Margin="12,4"> <Grid Grid.Row="0" ColumnDefinitions="180,*" Margin="12,4"
IsVisible="{Binding IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Loc[contacts.name]}" FontWeight="Bold"/> <TextBlock Grid.Column="0" Text="{Binding Loc[contacts.name]}" FontWeight="Bold"/>
<TextBlock Grid.Column="1" Text="{Binding Loc[contacts.address]}" FontWeight="Bold"/> <TextBlock Grid.Column="1" Text="{Binding Loc[contacts.address]}" FontWeight="Bold"/>
</Grid> </Grid>
@@ -322,13 +452,25 @@
SelectedItem="{Binding SelectedContactInList}"> SelectedItem="{Binding SelectedContactInList}">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:ContactEntry"> <DataTemplate x:DataType="vm:ContactEntry">
<Grid ColumnDefinitions="180,*"> <Panel>
<!-- Desktop: 2 colonne -->
<Grid ColumnDefinitions="180,*"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Name}" <TextBlock Grid.Column="0" Text="{Binding Name}"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<SelectableTextBlock Grid.Column="1" Text="{Binding Address}" <SelectableTextBlock Grid.Column="1" Text="{Binding Address}"
FontFamily="monospace" FontSize="12" FontFamily="monospace" FontSize="12"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
</Grid> </Grid>
<!-- Mobile: card verticale -->
<StackPanel Spacing="1" Margin="0,3"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsMobile}">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" FontSize="13"/>
<SelectableTextBlock Text="{Binding Address}"
FontFamily="monospace" FontSize="11"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</Panel>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
@@ -337,8 +479,9 @@
Command="{Binding RemoveSelectedContactCommand}" Command="{Binding RemoveSelectedContactCommand}"
IsEnabled="{Binding SelectedContactInList, Converter={x:Static ObjectConverters.IsNotNull}}" IsEnabled="{Binding SelectedContactInList, Converter={x:Static ObjectConverters.IsNotNull}}"
Margin="0,6,0,0"/> Margin="0,6,0,0"/>
<!-- Form aggiungi contatto --> <!-- Form aggiungi contatto — Desktop: 3 colonne -->
<Grid Grid.Row="3" ColumnDefinitions="180,*,Auto" Margin="0,8,0,0"> <Grid Grid.Row="3" ColumnDefinitions="180,*,Auto" Margin="0,8,0,0"
IsVisible="{Binding IsDesktop}">
<TextBox Grid.Column="0" PlaceholderText="{Binding Loc[contacts.name.ph]}" <TextBox Grid.Column="0" PlaceholderText="{Binding Loc[contacts.name.ph]}"
Text="{Binding NewContactName}" Margin="0,0,6,0"/> Text="{Binding NewContactName}" Margin="0,0,6,0"/>
<TextBox Grid.Column="1" PlaceholderText="{Binding Loc[contacts.address.ph]}" <TextBox Grid.Column="1" PlaceholderText="{Binding Loc[contacts.address.ph]}"
@@ -347,6 +490,18 @@
<Button Grid.Column="2" Content="{Binding Loc[contacts.add]}" <Button Grid.Column="2" Content="{Binding Loc[contacts.add]}"
Command="{Binding AddContactCommand}"/> Command="{Binding AddContactCommand}"/>
</Grid> </Grid>
<!-- Form aggiungi contatto — Mobile: verticale -->
<StackPanel Grid.Row="3" Spacing="6" Margin="0,8,0,0"
IsVisible="{Binding IsMobile}">
<TextBox PlaceholderText="{Binding Loc[contacts.name.ph]}"
Text="{Binding NewContactName}"/>
<TextBox PlaceholderText="{Binding Loc[contacts.address.ph]}"
Text="{Binding NewContactAddress}" FontFamily="monospace"/>
<Button Content="{Binding Loc[contacts.add]}"
Command="{Binding AddContactCommand}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"/>
</StackPanel>
</Grid> </Grid>
</TabItem> </TabItem>
@@ -385,9 +540,10 @@
IsVisible="{Binding AddressInfo, Converter={x:Static ObjectConverters.IsNotNull}}"> IsVisible="{Binding AddressInfo, Converter={x:Static ObjectConverters.IsNotNull}}">
<Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}" <Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
BorderBrush="Gray" BorderThickness="1" CornerRadius="8" BorderBrush="Gray" BorderThickness="1" CornerRadius="8"
Width="560" Padding="0" MaxWidth="540" Margin="16"
HorizontalAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center"
DataContext="{Binding AddressInfo}"> DataContext="{Binding AddressInfo}">
<ScrollViewer MaxHeight="640">
<StackPanel Margin="24" Spacing="16"> <StackPanel Margin="24" Spacing="16">
<TextBlock Text="{Binding Loc[addr.info.title]}" <TextBlock Text="{Binding Loc[addr.info.title]}"
FontSize="18" FontWeight="Bold"/> FontSize="18" FontWeight="Bold"/>
@@ -428,6 +584,7 @@
HorizontalAlignment="Right" HorizontalAlignment="Right"
Command="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).CloseAddressInfoCommand}"/> Command="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).CloseAddressInfoCommand}"/>
</StackPanel> </StackPanel>
</ScrollViewer>
</Border> </Border>
</Border> </Border>
@@ -440,7 +597,7 @@
IsVisible="{Binding IsTxDetailsOpen}"> IsVisible="{Binding IsTxDetailsOpen}">
<Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}" <Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
BorderBrush="Gray" BorderThickness="1" CornerRadius="8" BorderBrush="Gray" BorderThickness="1" CornerRadius="8"
Width="640" MaxHeight="600" Padding="0" MaxWidth="540" MaxHeight="600" Margin="16" Padding="0"
HorizontalAlignment="Center" VerticalAlignment="Center"> HorizontalAlignment="Center" VerticalAlignment="Center">
<Panel Margin="24"> <Panel Margin="24">
@@ -564,14 +721,16 @@
IsVisible="{Binding IsServerSettingsOpen}"> IsVisible="{Binding IsServerSettingsOpen}">
<Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}" <Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
BorderBrush="Gray" BorderThickness="1" CornerRadius="8" BorderBrush="Gray" BorderThickness="1" CornerRadius="8"
Width="560" MaxHeight="540" MaxWidth="540" MaxHeight="540" Margin="16"
HorizontalAlignment="Center" VerticalAlignment="Center"> HorizontalAlignment="Center" VerticalAlignment="Center">
<ScrollViewer>
<StackPanel Margin="24" Spacing="14"> <StackPanel Margin="24" Spacing="14">
<TextBlock Text="{Binding Loc[server.title]}" <TextBlock Text="{Binding Loc[server.title]}"
FontSize="18" FontWeight="Bold"/> FontSize="18" FontWeight="Bold"/>
<!-- Host + porta separati --> <!-- Host + porta — Desktop: 3 colonne -->
<Grid ColumnDefinitions="*,Auto,Auto" RowDefinitions="Auto,Auto"> <Grid ColumnDefinitions="*,Auto,Auto" RowDefinitions="Auto,Auto"
IsVisible="{Binding IsDesktop}">
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Loc[server.host]}" <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Loc[server.host]}"
FontSize="11" Foreground="Gray"/> FontSize="11" Foreground="Gray"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Loc[server.port]}" <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Loc[server.port]}"
@@ -585,9 +744,29 @@
<CheckBox Grid.Row="1" Grid.Column="2" IsChecked="{Binding UseSsl}" <CheckBox Grid.Row="1" Grid.Column="2" IsChecked="{Binding UseSsl}"
Margin="10,2,0,0" VerticalAlignment="Center"/> Margin="10,2,0,0" VerticalAlignment="Center"/>
</Grid> </Grid>
<!-- Host + porta — Mobile: verticale -->
<StackPanel Spacing="8" IsVisible="{Binding IsMobile}">
<StackPanel Spacing="3">
<TextBlock Text="{Binding Loc[server.host]}" FontSize="11" Foreground="Gray"/>
<TextBox Text="{Binding ServerHost}" FontFamily="monospace"/>
</StackPanel>
<Grid ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0" Spacing="3">
<TextBlock Text="{Binding Loc[server.port]}" FontSize="11" Foreground="Gray"/>
<TextBox Text="{Binding ServerPort}" FontFamily="monospace"/>
</StackPanel>
<StackPanel Grid.Column="1" Spacing="3" Margin="16,0,0,0"
VerticalAlignment="Center">
<TextBlock Text="TLS" FontSize="11" Foreground="Gray"
HorizontalAlignment="Center"/>
<CheckBox IsChecked="{Binding UseSsl}"
HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
</StackPanel>
<!-- Azioni --> <!-- Azioni — Desktop: in riga -->
<StackPanel Orientation="Horizontal" Spacing="8"> <StackPanel Orientation="Horizontal" Spacing="8" IsVisible="{Binding IsDesktop}">
<Button Content="{Binding Loc[wallet.connect]}" Classes="accent" <Button Content="{Binding Loc[wallet.connect]}" Classes="accent"
Command="{Binding ConnectAndSyncCommand}" IsEnabled="{Binding !IsSyncing}"/> Command="{Binding ConnectAndSyncCommand}" IsEnabled="{Binding !IsSyncing}"/>
<Button Content="{Binding Loc[wallet.discover]}" <Button Content="{Binding Loc[wallet.discover]}"
@@ -595,6 +774,18 @@
<Button Content="{Binding Loc[wallet.resetcert]}" <Button Content="{Binding Loc[wallet.resetcert]}"
Command="{Binding ResetCertificatesCommand}"/> Command="{Binding ResetCertificatesCommand}"/>
</StackPanel> </StackPanel>
<!-- Azioni — Mobile: in colonna -->
<StackPanel Spacing="6" IsVisible="{Binding IsMobile}">
<Button Content="{Binding Loc[wallet.connect]}" Classes="accent"
Command="{Binding ConnectAndSyncCommand}" IsEnabled="{Binding !IsSyncing}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
<Button Content="{Binding Loc[wallet.discover]}"
Command="{Binding DiscoverServersCommand}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
<Button Content="{Binding Loc[wallet.resetcert]}"
Command="{Binding ResetCertificatesCommand}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
</StackPanel>
<!-- Stato connessione --> <!-- Stato connessione -->
<StackPanel Orientation="Horizontal" Spacing="6"> <StackPanel Orientation="Horizontal" Spacing="6">
@@ -624,16 +815,35 @@
</ListBox.Styles> </ListBox.Styles>
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate x:DataType="net:KnownServer"> <DataTemplate x:DataType="net:KnownServer">
<Grid ColumnDefinitions="*,Auto"> <Panel>
<!-- Desktop: host + porte in 2 colonne -->
<Grid ColumnDefinitions="*,Auto"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsDesktop}">
<TextBlock Grid.Column="0" Text="{Binding Host}" <TextBlock Grid.Column="0" Text="{Binding Host}"
FontFamily="monospace" FontSize="13" FontFamily="monospace" FontSize="13"
VerticalAlignment="Center"/> VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" FontSize="11" Foreground="Gray" <TextBlock Grid.Column="1" FontSize="11" Foreground="Gray"
VerticalAlignment="Center"> VerticalAlignment="Center">
<Run Text="tcp "/><Run Text="{Binding TcpPort}"/> <Run Text="tcp "/><Run Text="{Binding TcpPort}"/>
<Run Text=" / ssl"/><Run Text="{Binding SslPort}"/> <Run Text=" ssl "/><Run Text="{Binding SslPort}"/>
</TextBlock> </TextBlock>
</Grid> </Grid>
<!-- Mobile: host sopra, porte sotto -->
<StackPanel Spacing="1" Margin="0,3"
IsVisible="{Binding $parent[UserControl].((vm:MainWindowViewModel)DataContext).IsMobile}">
<TextBlock Text="{Binding Host}"
FontFamily="monospace" FontSize="13"
TextTrimming="CharacterEllipsis"/>
<StackPanel Orientation="Horizontal" Spacing="12">
<TextBlock FontSize="11" Foreground="Gray">
<Run Text="tcp "/><Run Text="{Binding TcpPort}"/>
</TextBlock>
<TextBlock FontSize="11" Foreground="Gray">
<Run Text="ssl "/><Run Text="{Binding SslPort}"/>
</TextBlock>
</StackPanel>
</StackPanel>
</Panel>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
@@ -642,6 +852,7 @@
HorizontalAlignment="Right" HorizontalAlignment="Right"
Command="{Binding CloseServerSettingsCommand}"/> Command="{Binding CloseServerSettingsCommand}"/>
</StackPanel> </StackPanel>
</ScrollViewer>
</Border> </Border>
</Border> </Border>
@@ -653,8 +864,9 @@
IsVisible="{Binding IsSettingsOpen}"> IsVisible="{Binding IsSettingsOpen}">
<Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}" <Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
BorderBrush="Gray" BorderThickness="1" CornerRadius="8" BorderBrush="Gray" BorderThickness="1" CornerRadius="8"
Width="460" MaxWidth="440" Margin="16"
HorizontalAlignment="Center" VerticalAlignment="Center"> HorizontalAlignment="Center" VerticalAlignment="Center">
<ScrollViewer MaxHeight="640">
<StackPanel Margin="24" Spacing="16"> <StackPanel Margin="24" Spacing="16">
<TextBlock Text="{Binding Loc[settings.title]}" <TextBlock Text="{Binding Loc[settings.title]}"
FontSize="18" FontWeight="Bold"/> FontSize="18" FontWeight="Bold"/>
@@ -712,6 +924,7 @@
HorizontalAlignment="Right" HorizontalAlignment="Right"
Command="{Binding CloseSettingsCommand}"/> Command="{Binding CloseSettingsCommand}"/>
</StackPanel> </StackPanel>
</ScrollViewer>
</Border> </Border>
</Border> </Border>
@@ -723,7 +936,7 @@
IsVisible="{Binding IsHelpOpen}"> IsVisible="{Binding IsHelpOpen}">
<Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}" <Border Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}"
BorderBrush="Gray" BorderThickness="1" CornerRadius="8" BorderBrush="Gray" BorderThickness="1" CornerRadius="8"
Width="460" MaxWidth="440" Margin="16"
HorizontalAlignment="Center" VerticalAlignment="Center"> HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Margin="24" Spacing="16"> <StackPanel Margin="24" Spacing="16">
<TextBlock Text="{Binding Loc[help.title]}" <TextBlock Text="{Binding Loc[help.title]}"