zlgopen / awtk-mvvm

Model-View-ViewModel for AWTK
GNU Lesser General Public License v2.1
86 stars 28 forks source link

单个view-model如何显示两列表? #62

Open ufbycd opened 3 weeks ago

ufbycd commented 3 weeks ago

两个列表相互关联,所以让单个view-model显示两列表。我按如下实现了view-model,初始化时两个表示的内容显示正确,但列表内容修改后,列表在显示上没有更新。什么问题?

<dialog x="c" y="m" w="50%" h="80%" v-model="assay_selector">
    <dialog_title name="title" x="0" y="0" w="100%" h="40" tr_text="选择检测项目" />
    <dialog_client x="0" y="40" w="100%" h="-100" children_layout="default(r=1,c=2,s=10)">
        <list_view item_height="50">
            <label x="0" y="0" w="100%" h="50" style="table_title" tr_text="检测项目" />

            <scroll_view name="names" x="0" y="50" w="100%" h="-50">
                <list_item v-for="{names}" children_layout="default(r=1,c=1)"
                    v-on:click="{select_name, Args=fscript?index=index, IsContinue=true}">
                    <property name="v-data:style">
                <![CDATA[ {(index == selected_name_index) ? "selected" : "default"} ]]>
                    </property>
                    <label v-data:text="{item}" />
                </list_item>
            </scroll_view>

            <scroll_bar_m x="right" y="50" w="9" h="-50" value="0" />
        </list_view>

        <list_view item_height="50">
            <label x="0" y="0" w="100%" h="50" style="table_title" tr_text="批次" />

            <scroll_view name="lots" x="0" y="50" w="100%" h="-50">
                <list_item v-for="{lots}" children_layout="default(r=1,c=1)"
                    v-on:click="{select_lot, Args=fscript?index=index, IsContinue=true}">
                    <property name="v-data:style">
            <![CDATA[ {(index == selected_lot_index) ? "selected" : "default"} ]]>
                    </property>
                    <label v-data:text="{item}" />
                </list_item>
            </scroll_view>

            <scroll_bar_m x="right" y="50" w="9" h="-50" value="0" />
        </list_view>
    </dialog_client>

    <button name="ok" x="40" y="bottom:10" w="200" h="40" tr_text="确定" v-on:click="{confirm}" />
    <button name="cancel" x="right:40" y="bottom:10" w="200" h="40" tr_text="取消" on:click="close()" />
</dialog>
#include "assay_selector_vm.h"
#include "repository/assay_repository.h"
#include "common/common.h"
#include "pubsub.h"

#include "mvvm/base/view_model_array.h"
#include "mvvm/base/navigator.h"
#include "mvvm/base/utils.h"

typedef struct assay_selector_vm {
  view_model_array_t view_model_array;

  tk_object_t *names;
  tk_object_t *lots;
  int selected_name_index;
  int selected_lot_index;
} assay_selector_vm_t;

static assay_selector_vm_t* _cast(tk_object_t* obj);

static ret_t _get_prop(tk_object_t* obj, const char* name, value_t* v) {
  assay_selector_vm_t* asvm = _cast(obj);
  return_value_if_fail(asvm != NULL, RET_BAD_PARAMS);
  ret_t ret = RET_NOT_FOUND;

  log_debug("%s: %s\n", __func__, name);
  if(view_model_array_default_get_prop(VIEW_MODEL(obj), name, v) == RET_OK) {
    ret = RET_OK;
  }

  else if (tk_str_eq(name, "names")) {
    value_set_object(v, asvm->names);
    ret = RET_OK;
  } else if(tk_str_eq(name, "lots")) {
      value_set_object(v, asvm->lots);
      ret = RET_OK;
  } else if(tk_str_eq(name, "selected_name_index")) {
      value_set_int(v, asvm->selected_name_index);
      ret = RET_OK;
  } else if(tk_str_eq(name, "selected_lot_index")) {
      value_set_int(v, asvm->selected_lot_index);
      ret = RET_OK;
  }

  // names.[<index>]
  else if(tk_str_start_with(name, "names.")) {
      ret = tk_object_get_prop(asvm->names, name + 6, v);
  }

  // lots.[<index>]
  else if(tk_str_start_with(name, "lots.")) {
      ret = tk_object_get_prop(asvm->lots, name + 5, v);
  }

  return ret;
}

static ret_t _set_prop(tk_object_t* obj, const char* name, const value_t* v) {
    return RET_NOT_IMPL;
}

static ret_t _remove_prop(tk_object_t* obj, const char* name) {
    return RET_NOT_IMPL;
}

static bool_t _can_exec(tk_object_t* obj, const char* name, const char* args) {
  assay_selector_vm_t* asvm = _cast(obj);
  return_value_if_fail(asvm != NULL, false);
  bool_t b = false;

  if(tk_str_eq(name, "select_name")) {
      b = true;
  } else if(tk_str_eq(name, "select_lot")) {
      b = true;
  } else if(tk_str_eq(name, "confirm")) {
    uint32_t names_len = tk_object_get_prop_uint32(asvm->names, TK_OBJECT_PROP_SIZE, 0);
    uint32_t lots_len = tk_object_get_prop_uint32(asvm->lots, TK_OBJECT_PROP_SIZE, 0);

    b = (asvm->selected_name_index >= 0) &&
              (asvm->selected_lot_index >= 0) &&
              (asvm->selected_name_index < names_len) &&
              (asvm->selected_lot_index < lots_len);
  }

  return b;
}

static const char* _get_item_name(assay_selector_vm_t* asvm, int i) {
    char buf[16];

    tk_snprintf(buf, sizeof(buf), "[%d]", i);
    const char* name = tk_object_get_prop_str(asvm->names, buf);

    return (name != NULL) ? name : "";
}

static int _get_item_lot(assay_selector_vm_t* asvm, int i) {
    char buf[16];

    tk_snprintf(buf, sizeof(buf), "[%d]", i);
    return tk_object_get_prop_int(asvm->names, buf, 0);
}

static ret_t _exec(tk_object_t* obj, const char* name, const char* args) {
  ret_t ret = RET_NOT_IMPL;
  assay_selector_vm_t* asvm = _cast(obj);
  return_value_if_fail(asvm != NULL, RET_BAD_PARAMS);

  if(tk_str_eq(name, "select_name")) {
      tk_object_t* a = object_default_create();
      tk_command_arguments_to_object(args, a);
      int selected_index = tk_object_get_prop_int32(a, "index", asvm->selected_name_index);
      tk_object_unref(a);

      if(asvm->selected_name_index == selected_index) {
          ret = RET_OK;
      } else {
          asvm->selected_name_index = selected_index;

          char where[64];
          const char* selected_name = _get_item_name(asvm, asvm->selected_name_index);

          tk_snprintf(where, sizeof(where), "name='%s'", selected_name);
          assay_repository_select_rows("lot", where, asvm->lots);
          asvm->selected_lot_index = -1;

          uint32_t lots_size = tk_object_get_prop_uint32(asvm->lots, TK_OBJECT_PROP_SIZE, 0);
          log_debug("lot list size: %u\n", lots_size);

          ret = RET_ITEMS_CHANGED;
      }
  } else if(tk_str_eq(name, "select_lot")) {
      tk_object_t* a = object_default_create();
      tk_command_arguments_to_object(args, a);
      int selected_index = tk_object_get_prop_int32(a, "index", asvm->selected_lot_index);
      tk_object_unref(a);

      if(asvm->selected_lot_index == selected_index) {
          ret = RET_OK;
      } else {
          asvm->selected_lot_index = selected_index;
          ret = RET_OBJECT_CHANGED;
      }
  } else if(tk_str_eq(name, "confirm")) {
      assay_id_t aid;

      tk_strncpy(aid.name, _get_item_name(asvm, asvm->selected_name_index), sizeof(aid.name));
      aid.lot = _get_item_lot(asvm, asvm->selected_lot_index);
      pubsub_publish(pubsub_topic_selected_assay, &aid, sizeof(aid));
      ret = navigator_back();
  }

  if (ret == RET_OBJECT_CHANGED) {
//    emitter_dispatch_simple_event(EMITTER(obj), EVT_PROPS_CHANGED);
//    emitter_dispatch_simple_event(EMITTER(obj), EVT_ITEMS_CHANGED);
      ret = RET_ITEMS_CHANGED;
  }

  return ret;
}

static ret_t _destroy(tk_object_t* obj) {
  assay_selector_vm_t* asvm = _cast(obj);
  return_value_if_fail(asvm != NULL, RET_BAD_PARAMS);

  tk_object_unref(asvm->names);
  tk_object_unref(asvm->lots);

  return RET_OK;
}

static const object_vtable_t _vtable = {
    .type = "assay_selector_vm_t",
    .desc = "assay selector VM",
    .size = sizeof(assay_selector_vm_t),
    .is_collection = false,
    .exec = _exec,
    .can_exec = _can_exec,
    .remove_prop = _remove_prop,
    .get_prop = _get_prop,
    .set_prop = _set_prop,
    .on_destroy = _destroy,
};

static assay_selector_vm_t* _cast(tk_object_t* obj) {
  return_value_if_fail(obj != NULL && obj->vt == &_vtable, NULL);

  return (assay_selector_vm_t*)obj;
}

static const char* _preprocess_prop(view_model_t* view_model, const char* prop) {
  char index[TK_NUM_MAX_LEN + 1];
  view_model_array_t* vm_array = VIEW_MODEL_ARRAY(view_model);
  return_value_if_fail(view_model != NULL && prop != NULL, NULL);

  if (tk_str_eq(prop, "item")) {
      tk_snprintf(index, TK_NUM_MAX_LEN, "[%d]", vm_array->cursor);
      str_set(&(vm_array->temp_prop), prop);
      str_replace(&(vm_array->temp_prop), "item", index);

      return vm_array->temp_prop.str;
  } else if (tk_str_start_with(prop, "item")) {
    tk_snprintf(index, TK_NUM_MAX_LEN, "[%d].", vm_array->cursor);
    str_set(&(vm_array->temp_prop), prop);
    str_replace(&(vm_array->temp_prop), "item_", index);
    str_replace(&(vm_array->temp_prop), "item.", index);

    return vm_array->temp_prop.str;
  } else if(tk_str_start_with(prop, "selected.")) {
    tk_snprintf(index, TK_NUM_MAX_LEN, "[%d].", vm_array->selected_index);
    str_set(&(vm_array->temp_prop), prop);
    str_replace(&(vm_array->temp_prop), "selected.", index);

    return vm_array->temp_prop.str;
  } else {
    return prop;
  }
}

view_model_t* assay_selector_vm_create(navigator_request_t* req) {
  tk_object_t* obj = tk_object_create(&_vtable);
  view_model_t* vm = view_model_array_init(VIEW_MODEL(obj));
  assay_selector_vm_t* asvm =  _cast(obj);
  return_value_if_fail(asvm != NULL, NULL);

  vm->preprocess_prop = _preprocess_prop;

  asvm->names = object_array_create();
  asvm->lots = object_array_create();
  asvm->selected_name_index = -1;
  asvm->selected_lot_index = -1;

  assay_repository_select_rows("name", NULL, asvm->names);
  assay_repository_select_rows("lot", "name='CK-MB'", asvm->lots);

  return vm;
}
xianjimli commented 2 weeks ago

不行。你可以用两个ViewModel对应到同一个Model上。 view1 -> view_model1 -> model view2 -> view_model2 -> model