diff --git a/dist/DatabaseApp.exe b/dist/DatabaseApp.exe index 4155aa8..b7e71ff 100644 Binary files a/dist/DatabaseApp.exe and b/dist/DatabaseApp.exe differ diff --git a/main.py b/main.py index 8ed163e..f76dccd 100644 --- a/main.py +++ b/main.py @@ -22,164 +22,231 @@ class DatabaseApp(App): self.active_numpad_input = None def build(self): - # Set window to fullscreen + # Set window to fullscreen first so Window.height reflects the screen Window.fullscreen = 'auto' - # Root float layout so we can overlay the exit button + # ------------------------------------------------------------------ + # Responsive sizing: derive all dimensions from the actual screen height + # so the layout fits on any display (800p, 900p, 1080p, etc.) + # ------------------------------------------------------------------ + wh = Window.height # actual screen height after fullscreen + + # Numpad occupies 29% of screen height (fixed proportion) + h_numpad_wr = int(wh * 0.29) + + # Main layout outer padding and spacing (scaled) + s = max(0.65, min(1.0, wh / 1080.0)) + pad_v = max(8, int(20 * s)) # top / bottom padding + pad_h = max(8, int(30 * s)) # left / right padding + m_spacing = max(4, int(10 * s)) # gap between content and numpad + + # Space available for the 6 content rows (after numpad + padding + gap) + avail_total = wh - h_numpad_wr - 2 * pad_v - m_spacing + c_spacing = max(4, int(12 * s)) # gap between content rows + avail_items = avail_total - c_spacing * 5 # 6 rows → 5 gaps + + # Distribute height proportionally among rows + # Reference weights: title=50, search=100, mode=38, buttons=65, update=187, status=40 + _w = [50, 100, 38, 65, 187, 40] + _t = sum(_w) + def _h(weight): + return max(24, int(avail_items * weight / _t)) + + h_title = _h(_w[0]) + h_search = _h(_w[1]) + h_row = max(20, h_search // 2) + h_mode = _h(_w[2]) + h_buttons = _h(_w[3]) + h_update = _h(_w[4]) + h_status = _h(_w[5]) + + # Update-frame internal heights + upd_pad = max(4, int(8 * s)) + upd_spc = max(4, int(8 * s)) + h_upd_title = max(20, int(h_update * 0.20)) + h_upd_row = max(20, int(h_update * 0.24)) + h_upd_inputs = h_upd_row * 2 + upd_spc + h_upd_btns = max(28, h_update - h_upd_title - h_upd_inputs - 2*upd_pad - 2*upd_spc) + + # Numpad internal heights + np_pad_v = max(3, int(6 * s)) + np_spc = max(3, int(6 * s)) + h_enter_btn = max(34, int(h_numpad_wr * 0.24)) + h_numpad_gr = h_numpad_wr - h_enter_btn - 2 * np_pad_v - np_spc + + # Font sizes (scaled) + f_title = max(14, int(26 * s)) + f_normal = max(11, int(18 * s)) + f_btn = max(12, int(20 * s)) + f_numpad = max(15, int(26 * s)) + f_enter = max(14, int(24 * s)) + f_mode = max(10, int(16 * s)) + f_override = max(10, int(14 * s)) + f_status = max(12, int(20 * s)) + sp = max(6, int(10 * s)) # generic widget spacing + + # ------------------------------------------------------------------ + # Build UI + # ------------------------------------------------------------------ root = FloatLayout() - # Main layout with better spacing for fullscreen - main_layout = BoxLayout(orientation='vertical', padding=40, spacing=20, - size_hint=(1, 1), pos_hint={'x': 0, 'y': 0}) - - # Top spacer (reduced) - main_layout.add_widget(Label(size_hint_y=0.03)) - - # Content container - centered - content_layout = BoxLayout(orientation='vertical', spacing=30, size_hint_y=None) + main_layout = BoxLayout( + orientation='vertical', + padding=[pad_h, pad_v, pad_h, pad_v], + spacing=m_spacing, + size_hint=(1, 1), + pos_hint={'x': 0, 'y': 0} + ) + + # --- Content container (fills remaining space above numpad) --- + content_layout = BoxLayout(orientation='vertical', spacing=c_spacing, size_hint_y=None) content_layout.bind(minimum_height=content_layout.setter('height')) - - # Title row: title label + Exit button on the right - title_row = BoxLayout(orientation='horizontal', size_hint_y=None, height=50, spacing=15) - title = Label(text='Database Search & Update', font_size=28, bold=True) + + # Title row: title + Exit button + title_row = BoxLayout(orientation='horizontal', size_hint_y=None, height=h_title, spacing=sp) + title = Label(text='Database Search & Update', font_size=f_title, bold=True) title_row.add_widget(title) exit_btn = Button( text='Exit', - font_size=18, + font_size=max(11, int(16 * s)), bold=True, background_color=(0.85, 0.1, 0.1, 1), color=(1, 1, 1, 1), size_hint_x=None, - width=110 + width=max(70, int(100 * s)) ) exit_btn.bind(on_press=lambda x: self.stop()) title_row.add_widget(exit_btn) content_layout.add_widget(title_row) - - # Search section - search_layout = GridLayout(cols=2, size_hint_y=None, height=100, spacing=15, row_force_default=True, row_default_height=45) - search_layout.add_widget(Label(text='ID:', size_hint_x=0.25, font_size=20, bold=True)) + + # Search section (ID + Mass) + search_layout = GridLayout( + cols=2, size_hint_y=None, height=h_search, + spacing=sp, row_force_default=True, row_default_height=h_row + ) + search_layout.add_widget(Label(text='ID:', size_hint_x=0.25, font_size=f_normal, bold=True)) self.id_input = TextInput( - multiline=False, - size_hint_x=0.75, - hint_text='Enter ID and press Enter to search (max 20 chars)', - readonly=False, - font_size=20, - padding=[10, 10] + multiline=False, size_hint_x=0.75, + hint_text='Enter ID (max 20 chars)', + readonly=False, font_size=f_normal, padding=[7, 7] ) self.id_input.bind(on_text_validate=self.search_record) self.id_input.bind(on_text=self.update_mode_indicator) self.id_input.bind(focus=self.on_id_input_focus) search_layout.add_widget(self.id_input) - search_layout.add_widget(Label(text='Mass:', size_hint_x=0.25, font_size=20, bold=True)) + search_layout.add_widget(Label(text='Mass:', size_hint_x=0.25, font_size=f_normal, bold=True)) self.mass_input = TextInput( - multiline=False, - size_hint_x=0.75, - hint_text='Mass value (read-only)', - readonly=True, - font_size=20, - padding=[10, 10] + multiline=False, size_hint_x=0.75, + hint_text='Mass (read-only)', + readonly=True, font_size=f_normal, padding=[7, 7] ) search_layout.add_widget(self.mass_input) content_layout.add_widget(search_layout) - # Mode indicator row: label + override button - self.manual_override = None # None = auto, 'BOX' or 'PRODUCT' = manual - mode_row = BoxLayout(orientation='horizontal', size_hint_y=None, height=40, spacing=15) + # Mode indicator row + self.manual_override = None + mode_row = BoxLayout(orientation='horizontal', size_hint_y=None, height=h_mode, spacing=sp) self.mode_label = Label( text='Article type detected: PRODUCT', - size_hint_x=0.75, - font_size=18, - bold=True, - color=(0.4, 0.8, 1, 1) + size_hint_x=0.75, font_size=f_mode, bold=True, color=(0.4, 0.8, 1, 1) ) mode_row.add_widget(self.mode_label) self.override_btn = Button( - text='Override type', - size_hint_x=0.25, - font_size=16, - bold=True, - background_color=(0.3, 0.3, 0.3, 1) + text='Override type', size_hint_x=0.25, + font_size=f_override, bold=True, background_color=(0.3, 0.3, 0.3, 1) ) self.override_btn.bind(on_press=self.toggle_override) mode_row.add_widget(self.override_btn) content_layout.add_widget(mode_row) - # Button section - larger buttons (3 columns now) - button_layout = GridLayout(cols=3, size_hint_y=None, height=70, spacing=15) - add_update_btn = Button(text='Add/Update', font_size=22, bold=True) + # Action buttons (Add/Update, Reset, Settings) + button_layout = GridLayout(cols=3, size_hint_y=None, height=h_buttons, spacing=sp) + add_update_btn = Button(text='Add/Update', font_size=f_btn, bold=True) add_update_btn.bind(on_press=self.show_update_frame) button_layout.add_widget(add_update_btn) - reset_btn = Button(text='Reset Values', font_size=22, bold=True) + reset_btn = Button(text='Reset Values', font_size=f_btn, bold=True) reset_btn.bind(on_press=self.reset_values) button_layout.add_widget(reset_btn) - settings_btn = Button(text='Settings', font_size=22, bold=True) + settings_btn = Button(text='Settings', font_size=f_btn, bold=True) settings_btn.bind(on_press=self.show_settings) button_layout.add_widget(settings_btn) content_layout.add_widget(button_layout) - - # Minimal spacing between buttons and update frame - content_layout.add_widget(Label(size_hint_y=None, height=10)) - # Update frame (initially disabled) - self.update_frame = BoxLayout(orientation='vertical', padding=15, spacing=15, size_hint_y=None, height=200) - self.update_frame_label = Label(text='Update Values', size_hint_y=None, height=40, font_size=22, bold=True) + # Update frame + self.update_frame = BoxLayout( + orientation='vertical', padding=upd_pad, spacing=upd_spc, + size_hint_y=None, height=h_update + ) + self.update_frame_label = Label( + text='Update Values', size_hint_y=None, height=h_upd_title, + font_size=f_btn, bold=True + ) self.update_frame.add_widget(self.update_frame_label) - update_inputs = GridLayout(cols=2, spacing=15, size_hint_y=None, height=100, row_force_default=True, row_default_height=45) - update_inputs.add_widget(Label(text='ID:', size_hint_x=0.25, font_size=20, bold=True)) - self.update_id_input = TextInput(multiline=False, size_hint_x=0.75, readonly=True, font_size=20, padding=[10, 10]) + update_inputs = GridLayout( + cols=2, spacing=sp, size_hint_y=None, height=h_upd_inputs, + row_force_default=True, row_default_height=h_upd_row + ) + update_inputs.add_widget(Label(text='ID:', size_hint_x=0.25, font_size=f_normal, bold=True)) + self.update_id_input = TextInput( + multiline=False, size_hint_x=0.75, readonly=True, font_size=f_normal, padding=[7, 7] + ) update_inputs.add_widget(self.update_id_input) - update_inputs.add_widget(Label(text='Mass:', size_hint_x=0.25, font_size=20, bold=True)) - self.update_mass_input = TextInput(multiline=False, size_hint_x=0.75, readonly=True, font_size=20, padding=[10, 10]) + update_inputs.add_widget(Label(text='Mass:', size_hint_x=0.25, font_size=f_normal, bold=True)) + self.update_mass_input = TextInput( + multiline=False, size_hint_x=0.75, readonly=True, font_size=f_normal, padding=[7, 7] + ) self.update_mass_input.bind(focus=self.on_mass_input_focus) update_inputs.add_widget(self.update_mass_input) self.update_frame.add_widget(update_inputs) - # Add update and delete buttons in same row - update_buttons = GridLayout(cols=2, size_hint_y=None, height=60, spacing=15) - self.update_confirm_btn = Button(text='Confirm Add/Update', disabled=True, font_size=22, bold=True) + update_buttons = GridLayout(cols=2, size_hint_y=None, height=h_upd_btns, spacing=sp) + self.update_confirm_btn = Button(text='Confirm Add/Update', disabled=True, font_size=f_btn, bold=True) self.update_confirm_btn.bind(on_press=self.add_update_record) update_buttons.add_widget(self.update_confirm_btn) - self.delete_btn = Button(text='Delete', disabled=True, font_size=22, bold=True) + self.delete_btn = Button(text='Delete', disabled=True, font_size=f_btn, bold=True) self.delete_btn.bind(on_press=self.delete_record) update_buttons.add_widget(self.delete_btn) self.update_frame.add_widget(update_buttons) content_layout.add_widget(self.update_frame) - # Initially disable update frame self.set_update_frame_enabled(False) - # Status label - larger and more prominent + # Status label self.status_label = Label( - text='Ready', - size_hint_y=None, - height=50, - color=(0, 0.8, 0, 1), - font_size=22, - bold=True + text='Ready', size_hint_y=None, height=h_status, + color=(0, 0.8, 0, 1), font_size=f_status, bold=True ) content_layout.add_widget(self.status_label) - - main_layout.add_widget(content_layout) - # Numeric keypad — digits + decimal, backspace, enter - numpad_wrapper = BoxLayout(orientation='vertical', size_hint_y=None, height=320, spacing=8, padding=[40, 8, 40, 8]) - numpad = GridLayout(cols=3, size_hint_y=None, height=240, spacing=8) + # Wrap content in an AnchorLayout that fills all space above the numpad + # so the content block is vertically centred regardless of screen size + content_anchor = AnchorLayout(anchor_x='center', anchor_y='center', size_hint_y=1) + content_anchor.add_widget(content_layout) + main_layout.add_widget(content_anchor) + + # --- Numeric keypad --- + numpad_wrapper = BoxLayout( + orientation='vertical', size_hint_y=None, height=h_numpad_wr, + spacing=np_spc, padding=[pad_h, np_pad_v, pad_h, np_pad_v] + ) + numpad = GridLayout(cols=3, size_hint_y=None, height=h_numpad_gr, spacing=max(3, int(6 * s))) for digit in ['1', '2', '3', '4', '5', '6', '7', '8', '9']: - btn = Button(text=digit, font_size=28, bold=True) + btn = Button(text=digit, font_size=f_numpad, bold=True) btn.bind(on_press=self.numpad_press) numpad.add_widget(btn) - dot_btn = Button(text='.', font_size=28, bold=True, background_color=(0.25, 0.25, 0.45, 1)) + dot_btn = Button(text='.', font_size=f_numpad, bold=True, background_color=(0.25, 0.25, 0.45, 1)) dot_btn.bind(on_press=self.numpad_press) numpad.add_widget(dot_btn) - zero_btn = Button(text='0', font_size=28, bold=True) + zero_btn = Button(text='0', font_size=f_numpad, bold=True) zero_btn.bind(on_press=self.numpad_press) numpad.add_widget(zero_btn) - back_btn = Button(text='⌫', font_size=28, bold=True, background_color=(0.5, 0.3, 0.1, 1)) + back_btn = Button(text='⌫', font_size=f_numpad, bold=True, background_color=(0.5, 0.3, 0.1, 1)) back_btn.bind(on_press=self.numpad_backspace) numpad.add_widget(back_btn) numpad_wrapper.add_widget(numpad) - enter_btn = Button(text='Enter', font_size=26, bold=True, - background_color=(0.1, 0.55, 0.1, 1), size_hint_y=None, height=64) + enter_btn = Button( + text='Enter', font_size=f_enter, bold=True, + background_color=(0.1, 0.55, 0.1, 1), size_hint_y=None, height=h_enter_btn + ) enter_btn.bind(on_press=self.numpad_enter) numpad_wrapper.add_widget(enter_btn) main_layout.add_widget(numpad_wrapper)